目录

1. 抛javax.persistence.TransactionRequiredException异常,在没有事务时

2. 执行save/insert/delete相关的DB操作后,没有生效也没有错误

3. com.sun.proxy.$Proxyxxx cannot be cast to org.hibernate.engine.spi.SessionImplementor

4. org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias [xxx] 

5. org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread

6.  org.hibernate.QueryException: Legacy-style query parameters (`?`) are no longer supported; use JPA-style ordinal parameters (e.g., `?1`) instead

7. The increment size of the sequence is set to [50] in the entity mapping while the associated database sequence increment size is [1]

8.  主键唯一性约束

9.  批量更新方法NamedParameterJdbcTemplate.batchUpdate(Queries, batchArgs) 特别慢,有performance问题

10 项目里同时使用Mybatis和Hibernate, 那么transactionManager怎么配?

11. org.hibernate.exception.GenericJDBCException: could not insert: [CertainPackage.CertainTable_AUD]

12.  Configuring Ehcache as the Second-Level Cache Provider

13. Caused by: org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property

参考资料


版本信息

LibraryCurrentUpgraded
Hibernate-core3.6.10.Final5.4.18.final
Hibenrate-valicator4.2.0.Final6.1.5.final

1. 抛javax.persistence.TransactionRequiredException异常,在没有事务时

javax.persistence.TransactionRequiredException: Executing an update/delete query at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413)
at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1608)
at .....

相关的代码

	NativeQuery sql = session.createSQLQuery(query);
				sql.setProperties(params);
                sql.setFlushMode(FlushMode.MANUAL);
				return sql.executeUpdate();

原因是:Hibernate 5.3 之后遵循了JPA 规范,没有事务是不能更新的。

解决方法一: 加下面系统参数允许在是事务之外做更新操作。

hibernate.allow_update_outside_transaction=true

解决方法二: 在相关的方法上加@Transational 注解

参考资料是hibernate-orm/migration-guide.adoc at 5.2 · hibernate/hibernate-orm · GitHub

2. 执行save/insert/delete相关的DB操作后,没有生效也没有错误

相关Hibernate的代码如下:

session.saveOrUpdate(obj);

 session.merge(entity);

解决方法一: 加@Transational 注解在相关的方法上面

解决方法二: 加下面系统参数

hibernate.allow_update_outside_transaction=true

而且如果没有事务的话,要在增删改操作后面调用flush()方法

//session.saveOrUpdate(obj);
//session.merge(obj);
if(!session.getTransaction().isActive()) {
   session.flush();
}

3. com.sun.proxy.$Proxyxxx cannot be cast to org.hibernate.engine.spi.SessionImplementor

java.lang.ClassCastException: com.sun.proxy.$Proxy217 cannot be cast to org.hibernate.engine.spi.SessionImplementor
at org.hibernate.criterion.DetachedCriteria.getExecutableCriteria(DetachedCriteria.java:67)

原因是spring用了CGLIB 代理

解决方法一

把 template.execute(callback) 替换为template.executeWithNativeSession(callback)方法.

//template.execute(callback) 
template.executeWithNativeSession(callback)

在executeWithNativeSession方法里会会把做转换。

4. org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias [xxx] 

Caused by: org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias [NULL] during auto-discovery of a native-sql query
        at org.hibernate.loader.custom.CustomLoader.validateAliases(CustomLoader.java:520)
        at org.hibernate.loader.custom.CustomLoader.autoDiscoverTypes(CustomLoader.java:497)
        at org.hibernate.loader.Loader.preprocessResultSet(Loader.java:2357)
        at org.hibernate.loader.Loader.getResultSet(Loader.java:2313)
        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2064)
        at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2026)
        at org.hibernate.loader.Loader.doQuery(Loader.java:951)
        at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:352)
        at org.hibernate.loader.Loader.doList(Loader.java:2857)
        at org.hibernate.loader.Loader.doList(Loader.java:2839)
        at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2671)
        at org.hibernate.loader.Loader.list(Loader.java:2666)
        at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)
        at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2139)
        at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:1163)
        at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:173)
        at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1533)

问题代码

select .... ,null,null from table_name

解决方法:

select .... ,null as column2 ,null as column2 from table_name

5. org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread

错误信息

org.springframework.orm.hibernate5.HibernateSystemException: Could not obtain transaction-synchronized Session for current thread; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
        at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:300) ~[spring-orm-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
        at org.springframework.orm.hibernate5.HibernateTemplate.doExecute(HibernateTemplate.java:388) ~[spring-orm-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
        at org.springframework.orm.hibernate5.HibernateTemplate.execute(HibernateTemplate.java:337) ~[spring-orm-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]

....

Caused by: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
        at org.springframework.orm.hibernate5.SpringSessionContext.currentSession(SpringSessionContext.java:143) ~[spring-orm-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
        at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:475) ~[hibernate-core-5.4.18.Final.jar!/:5.4.18.Final]
        at org.hibernate.envers.AuditReaderFactory.get(AuditReaderFactory.java:38) ~[hibernate-envers-5.4.18.Final.jar!/:5.4.18.Final]
错误原因

  由错误提示可知,是不能获取事物同步Session

解决办法: 添加一个注解@Transactional在相关方法上, 例如下面代码

@Repository("studentDao")
@Transactional
public class StudentDaoImpl implements StudentDao
{

}

6.  org.hibernate.QueryException: Legacy-style query parameters (`?`) are no longer supported; use JPA-style ordinal parameters (e.g., `?1`) instead

错误信息

java.lang.IllegalArgumentException: org.hibernate.QueryException: Legacy-style query parameters (`?`) are no longer supported; use JPA-style ordinal parameters (e.g., `?1`) instead : xxxx此处省略涉及公司表的结果]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:138)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:725)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:113)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.hibernate5.HibernateTemplate$CloseSuppressingInvocationHandler.invoke(HibernateTemplate.java:1228)
at com.sun.proxy.$Proxy194.createQuery(Unknown Source)

java.lang.IllegalArgumentException: Could not locate ordinal parameter [0], expecting one of [1, 2, 3, 4, 5, 6, 7]
at org.hibernate.query.internal.ParameterMetadataImpl.getOrdinalParameterDescriptor(ParameterMetadataImpl.java:154) ~[hibernate-core-...
at org.hibernate.query.internal.NativeQueryImpl.setParameter(NativeQueryImpl.java:613) ~[hibernate-core-5.4.18.Final.jar!/:5.4.18.Final]
at org.hibernate.query.internal.NativeQueryImpl.setParameter(NativeQueryImpl.java:62) ~[hibernate-core-5.4.18.Final.jar!/:5.4.18.Final]

错误代码例如

SELECT u FROM User u WHERE u.userID = ?

原因是:hibernate 要遵守JPA标准, ?不是JPA的标准,

解决方法一: 使用JPA-Style ordinal parameters

SELECT u FROM User u WHERE u.userID = ?1

解决方法二: 使用 named parameters

SELECT u FROM User u WHERE u.userID = :userId

当然这种方法相应的代码也要改。再设置参数的适合按名字设置。

//SQLQuery sql = session.createSQLQuery(query);
// sql.setParameters(value, type);
SQLQuery sql = session.createSQLQuery(query);
sql.setParameter("userId" value, type);

7. The increment size of the sequence is set to [50] in the entity mapping while the associated database sequence increment size is [1]

原因是

hibernate.id.new_generator_mappings 这个值默认变为成true.

原来是用org.hibernate.id.SequenceHiLoGenerator。  现在因为hibernate.id.new_generator_mappings=true. 所以变成org.hibernate.id.enhanced.SequenceStyleGenerator

源代码

public class SequenceStyleGenerator
		implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, Configurable {
...
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {	
...
			switch ( sequenceMismatchStrategy ) {
					case EXCEPTION:
						throw new MappingException(
								String.format(
										"The increment size of the [%s] sequence is set to [%d] in the entity mapping " +
												"while the associated database sequence increment size is [%d].",
										databaseSequenceName, incrementSize, dbIncrementValue
								)
						);
					case FIX:
						incrementSize = dbIncrementValue;
					case LOG:
						LOG.sequenceIncrementSizeMismatch( databaseSequenceName, incrementSize, dbIncrementValue );
						break;
				}
			}

如果hibernate.id.new_generator_mappings=false 会有性能问题。尤其是在做db 的insert的时候。要通过调用select SEQ.nextval from DUAL取很多次primary key的值。

JPA SequenceGenerator with allocationSize 1 performance tuning – Life in USA

解决方法有两个

1.  设置下面系统参数,这样就不会报错了。但是有warning log .

        System.setProperty("hibernate.id.sequence.increment_size_mismatch_strategy", "LOG");

2. 重写创建sequence。 以50来增长。

--/
declare
maxval number;
begin
select (max(pk_user_id) + 2)*50 into maxval from user;
execute immediate 'DROP SEQUENCE SEQ_USER_ID ';
execute immediate 'CREATE SEQUENCE SEQ_USER_ID INCREMENT BY 50 START WITH '|| maxval ||' MAXVALUE 999999999999999999999999999 ';
end;
/

8.  主键唯一性约束

[https-jsse-nio-auto-1-exec-4](org.hibernate.engine.jdbc.spi.SqlExceptionHelper:137) - SQL Error: 1, SQLState: 23000
 ERROR[https-jsse-nio-auto-1-exec-4](org.hibernate.engine.jdbc.spi.SqlExceptionHelper:142) - ORA-00001: unique constraint (ASPEN.SYS_C00610415) violated

相关代码

@Id
@GeneratedValue(strategy = javax.persistence.GenerationType.SEQUENCE, generator = "generator")
@javax.persistence.SequenceGenerator(name = "generator", sequenceName = "SEQ_USER", allocationSize = 50)
@Column(name = "PK_USER_ID", unique = true, nullable = false)
public long getPkUserId() {

错误的原因是allocationSize = 50.  升级完Hibernate后, 因为有下面错误所以我当时把这个值改成allocationSize = 1了

The increment size of the sequence is set to [50] in the entity mapping while the associated database sequence increment size is [1]

原来是拿一个Sequence然后产生50个ID, 现在默认是一个Sequence对应于一个ID。所以要把Sequence 重建一下。 原来是49*(Sequence value)<Max(id) < 50*(Sequence value)

解决方法:基于表中最大的主键重建Sequence.  当然你也可以用你自己的方法重建。

--/
declare
maxval number;
begin
select max(pk_user_id) + 2 into maxval from user;
execute immediate 'DROP SEQUENCE SEQ_USER_ID ';
execute immediate 'CREATE SEQUENCE SEQ_USER_ID INCREMENT BY 1 START WITH '|| maxval ||' MAXVALUE 999999999999999999999999999 ';
end;
/

9.  批量更新方法NamedParameterJdbcTemplate.batchUpdate(Queries, batchArgs) 特别慢,有performance问题

原因: 如果你用oracle数据库,升级后如果你要查的数据是null并且没有设置类型的情况,

它开始从数据库里查询源数据数据的类型。而且每条数据都查。数据越多越慢。

相关类是org.springframework.jdbc.core.StatementCreatorUtils

private static void setNull(PreparedStatement ps, int paramIndex, int sqlType, @Nullable String typeName)
			throws SQLException {

		if (sqlType == SqlTypeValue.TYPE_UNKNOWN || (sqlType == Types.OTHER && typeName == null)) {
			boolean useSetObject = false;
			Integer sqlTypeToUse = null;
			if (!shouldIgnoreGetParameterType) {
				try {
					sqlTypeToUse = ps.getParameterMetaData().getParameterType(paramIndex);
				}
				catch (SQLException ex) {
					if (logger.isDebugEnabled()) {
						logger.debug("JDBC getParameterType call failed - using fallback method instead: " + ex);
					}
				}
			}
			if (sqlTypeToUse == null) {
				// Proceed with database-specific checks
				sqlTypeToUse = Types.NULL;
				DatabaseMetaData dbmd = ps.getConnection().getMetaData();
				String jdbcDriverName = dbmd.getDriverName();
				String databaseProductName = dbmd.getDatabaseProductName();
				if (databaseProductName.startsWith("Informix") ||
						(jdbcDriverName.startsWith("Microsoft") && jdbcDriverName.contains("SQL Server"))) {
						// "Microsoft SQL Server JDBC Driver 3.0" versus "Microsoft JDBC Driver 4.0 for SQL Server"
					useSetObject = true;
				}
				else if (databaseProductName.startsWith("DB2") ||
						jdbcDriverName.startsWith("jConnect") ||
						jdbcDriverName.startsWith("SQLServer")||
						jdbcDriverName.startsWith("Apache Derby")) {
					sqlTypeToUse = Types.VARCHAR;
				}
			}
			if (useSetObject) {
				ps.setObject(paramIndex, null);
			}
			else {
				ps.setNull(paramIndex, sqlTypeToUse);
			}
		}

解决方法一:加下面系统参数在spring boot 启动的时候

-Dspring.jdbc.getParameterType.ignore=true

解决方法二:

在调用NamedParameterJdbcTemplate.batchUpdate方法时把每一个列的类型也注入进去。

例如

		String sql = "insert into user(PK_USER_ID, USER_NAME, PASSWORD) VALUES (:userId, :name, :password)";
        List<MapSqlParameterSource> batchValues = new ArrayList<MapSqlParameterSource>(queryResult.size());
        for (Object[] objs : queryResult) {
        	MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource();
        	mapSqlParameterSource.addValue("userId", 1, Types.INTEGER);
        	mapSqlParameterSource.addValue("name", "name value", Types.VARCHAR);
        	mapSqlParameterSource.addValue("password", "password value", Types.VARCHAR);
        	batchValues.add(mapSqlParameterSource);
        }
		
		NamedParameterJdbcTemplate.batchUpdate(sql, batchValues.toArray(new MapSqlParameterSource[0]));

10 项目里同时使用Mybatis和Hibernate, 那么transactionManager怎么配?

问题代码:

	@Bean
	public HibernateTransactionManager transactionManager(@Qualifier("hibernateSessionFactory")	SessionFactory sessionFactory)

	{
		HibernateTransactionManager htm = new HibernateTransactionManager();
		htm.setSessionFactory(sessionFactory);
		return htm;
	}

	@Bean
	public DataSourceTransactionManager transactionManager(@Autowired @Qualifier("dataSource") DataSource dataSource) {
		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);		
		return transactionManager;
	}

如果你一起配置了HibernateTransactionManager 和DataSourceTransactionManager 这两个,如上,并且名字一样时。可能出现下面错误信息在你调用Flush()的时候。

javax.persistence.TransactionRequiredException: no transaction is in progress
        at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:413) ~[hibernate-core-5.4.18.Final.jar!/:5.4.18.Final]
        at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:3395) ~[hibernate-core-5.4.18.Final.jar!/:5.4.18.Final]
        at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1352) ~[hibernate-core-5.4.18.Final.jar!/:5.4.18.Final]
        at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1347) ~[hibernate-core-5.4.18.Final.jar!/:5.4.18.Final]

解决方法:

不要用Mybatis的TransactionManager, 直接用HibernateTransactionManager就可以了。

	@Bean
	public HibernateTransactionManager transactionManager(@Qualifier("hibernateSessionFactory")	SessionFactory sessionFactory)

	{
		HibernateTransactionManager htm = new HibernateTransactionManager();
		htm.setSessionFactory(sessionFactory);
		return htm;
	}

//	@Bean
//	public DataSourceTransactionManager transactionManager(@Autowired @Qualifier("dataSource") DataSource dataSource) {
//		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);		
//		return transactionManager;
//	}
<tx:annotation-driven transaction-manager="transactionManager"/>

或使用@EnableTransactionManagement

如果和其它的ORM技术和JDBC技术一起使用时的解决方法如下:

如果你采用了一个高端ORM技术(Hibernate、JPA、JDO),同时采用一个JDBC技术(Spring JDBC、iBatis),由于前者的会话(Session)是对后者连接(Connection)的封装,Spring会“足够智能地”在同一个事务线程让前者的会话封装后者的连接。所以,我们只要直接采用前者的事务管理器就可以了。表10-1给出了混合数据访问技术框架所对应的事务管理器。 

序    号混合数据访问技术框架事务管理器
1Hibernate+ Spring JDBC或iBatisorg.springframework.orm.hibernate5.HibernateTransactionManager
2JPA+Spring JDBC或iBatisorg.springframework.orm.jpa.JpaTransactionManager
3JDO+Spring JDBC或iBatisorg.springframework.orm.jdo.JdoTransactionManager

11. org.hibernate.exception.GenericJDBCException: could not insert: [CertainPackage.CertainTable_AUD]

错误信息:

org.springframework.orm.hibernate5.HibernateSystemException: Unable to perform beforeTransactionCompletion callback: org.hibernate.exception.GenericJDBCException: could not insert: [packageName.User_AUD]; nested exception is org.hibernate.HibernateException: Unable to perform beforeTransactionCompletion callback: org.hibernate.exception.GenericJDBCException: could not insert: [***packageName.User_AUD]
        at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:300) 
        at org.springframework.orm.hibernate5.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:804) ~[RELEASE]
        at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:635) 
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) 
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) 
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:633) 
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:386) 
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) 
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) 
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) 
                ....
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not insert: [packageName.User_AUD]
        at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154) 
        at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) 
        at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188) 
        at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1364) 
        at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1347) 
        at org.hibernate.envers.internal.synchronization.AuditProcess.doBeforeTransactionCompletion(AuditProcess.java:177) 
        at org.hibernate.envers.internal.synchronization.AuditProcessManager$1.doBeforeTransactionCompletion(AuditProcessManager.java:47) 
        at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:954) 
        at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:525) 
        at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2379) 
        at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:447) 
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183)
Caused by: java.sql.SQLException: Invalid column type: 16
        at oracle.jdbc.driver.OracleStatement.getInternalType(OracleStatement.java:4031) 
        at oracle.jdbc.driver.OraclePreparedStatement.setNullCritical(OraclePreparedStatement.java:4182) 
        at oracle.jdbc.driver.OraclePreparedStatement.setNull(OraclePreparedStatement.java:4166) 
        at oracle.jdbc.driver.OraclePreparedStatementWrapper.setNull(OraclePreparedStatementWrapper.java:1008) 
        at org.apache.commons.dbcp.DelegatingPreparedStatement.setNull(DelegatingPreparedStatement.java:108) 
        at org.apache.commons.dbcp.DelegatingPreparedStatement.setNull(DelegatingPreparedStatement.java:108) 
        at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:60) 
        at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:276) 
        at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:271) 
        at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:39) 
        at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2948) 
        at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3245) 

相关代码:

@Audited 是利用Hibernate 做审计。涉及到三个表t_user, revinfo, t_user_aud。 后面两个表是审计相关的表。

如果不了解怎么用@Audited做审计, 可以看这个博客:hibernate envers实践总结_keeppractice的博客-CSDN博客

@Entity
@Table(name = "t_user")
@Audited
@JsonIgnoreProperties(value = "hibernateLazyInitializer")
public class User {
 
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private boolean flag;

...
}

hibernate.dialect=org.hibernate.dialect.OracleDialect

错误原因:

如果应用org.hibernate.dialect.OracleDialect方言时,hibernate利用OracleDialect取出的boolean类型的描述器是BooleanTypeDescriptor ,BooleanTypeDescriptor 类型的描述器返回的类型是Types.Boolean. 这个类型16在OracleStatement里是没有的,所以会报错Invalid column type: 16

public class BooleanTypeDescriptor implements SqlTypeDescriptor {
	public static final BooleanTypeDescriptor INSTANCE = new BooleanTypeDescriptor();

	public BooleanTypeDescriptor() {
	}

	public int getSqlType() {
		return Types.BOOLEAN;
	}

abstract class OracleStatement
 int getInternalType(int paramInt) throws SQLException {
    char c = Character.MIN_VALUE;
    switch (paramInt) {
      case -7:
      case -6:
      case -5:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
        c = '\006';

java.sql.JDBCType

java.sql.Types

public class Types
    /**
     * The constant in the Java programming language, somtimes referred to
     * as a type code, that identifies the generic SQL type <code>BOOLEAN</code>.
     *
     * @since 1.4
     */
    public final static int BOOLEAN = 16;
    /**
 * <P>The constant in the Java programming language, sometimes referred
 * to as a type code, that identifies the generic SQL type
 * <code>BIT</code>.
 */
     public final static int BIT             =  -7;

如果是Oracle8iDialect 方言,它重写了getSqlTypeDescriptorOverride方法,取出的表述器是BitTypeDescriptor。对应的类型是7,Oracle8iDialect 之后的方言就不会有这个错误了

public class Oracle8iDialect extends Dialect {
	@Override
	protected SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) {
		return sqlCode == Types.BOOLEAN ? BitTypeDescriptor.INSTANCE : super.getSqlTypeDescriptorOverride( sqlCode );
	}

解决方法一:

把 hibernate.dialect=org.hibernate.dialect.OracleDialect 改为 hibernate.dialect=org.hibernate.dialect.Oracle12cDialect

其实改成>Oracle8iDialect 的方言就可以,因为比它新的方言类都是它的子类。

dialect  < Oracle8iDialect < Oracle9iDialect< Oracle10gDialect < Oracle12cDialect

解决方法二:

不要手动设置方言,直接去掉,因为spring可以自动根据你配置DB信息选择最新的方言。

org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver

org.hibernate.dialect.Database

	ORACLE {
		@Override
		public Class<? extends Dialect> latestDialect() {
			return Oracle12cDialect.class;
		}

		@Override
		public Dialect resolveDialect(DialectResolutionInfo info) {
			final String databaseName = info.getDatabaseName();

			if ( "Oracle".equals( databaseName ) ) {
				final int majorVersion = info.getDatabaseMajorVersion();

				switch ( majorVersion ) {
					case 12:
						return new Oracle12cDialect();
					case 11:
						// fall through
					case 10:
						return new Oracle10gDialect();
					case 9:
						return new Oracle9iDialect();
					case 8:
						return new Oracle8iDialect();
					default:
						return latestDialectInstance( this );

				}
			}

			return null;
		}
	},
....
	public abstract Class<? extends Dialect> latestDialect();

	public abstract Dialect resolveDialect(DialectResolutionInfo info);

	private static Dialect latestDialectInstance(Database database) {
		try {
			return database.latestDialect().newInstance();
		}
		catch (InstantiationException | IllegalAccessException e) {
			throw new HibernateException( e );
		}
	}

12.  Configuring Ehcache as the Second-Level Cache Provider

问题配置

 <property name="cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</property>

解决方法:

    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>

参考资料

Hibernate 4.x

For instance creation, use:

<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

To force Hibernate to use a singleton of Ehcache CacheManager, use:

<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>

Ehcachehttps://www.ehcache.org/generated/2.10.0/html/ehc-all/index.html#page/Ehcache_Documentation_Set/co-hib_configure_as_second_level_cache_provider.html%23wwconnect_header

13. Caused by: org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property

异常信息:

Caused by: org.hibernate.id.IdentifierGenerationException: attempted to
assign id from null one-to-one property

PrimaryKeyJoinColumn   violated - parent key not found

相关代码

public Class User{
    private int userId;
    private Document document;
	@Id
	@GeneratedValue(strategy = javax.persistence.GenerationType.SEQUENCE, generator = "generator")
	@javax.persistence.SequenceGenerator(name = "generator", sequenceName = "SEQ_USER_ID", allocationSize = 1)
	@Column(name = "PK_USER_ID", unique = true, nullable = false)
    public int getUserId(){ return userId;
    }

	@OneToOne()
	@PrimaryKeyJoinColumn(name="PK_USER_ID", referencedColumnName="PK_DOCUMENT_ID")
	@Cascade({org.hibernate.annotations.CascadeType.ALL})
    public Document getDocument(){
        return document;
    }
}

public class Document{
    private String documentId;
    private User user;
    @Id
    @GeneratedValue(generator="foreign")
    @GenericGenerator(name="foreign", strategy = "foreign", parameters={@Parameter(name="property", value="user")})
    public int getDocumentId(){
    return documentId;
    }
    @OneToOne(mappedBy="document", optional = false) 
    public User getUser() {
         return user;
     }
}

解决方法如下:

public Class User{
    private int userId;
    private Document document;
	@Id
	@GeneratedValue(strategy = javax.persistence.GenerationType.SEQUENCE, generator = "generator")
	@javax.persistence.SequenceGenerator(name = "generator", sequenceName = "SEQ_USER_ID", allocationSize = 1)
	@Column(name = "PK_USER_ID", unique = true, nullable = false)
    public int getUserId(){ return userId;
    }
     @OneToOne(cascade=CascadeType.ALL, mappedBy="user")
    public Document getDocument(){
        return document;
    }
}

public class Document{
    private String documentId;
    private User user;
    @Id
    @Column(name = "PK_DOCUMENT_ID")
    public int getDocumentId(){
    return documentId;
    }
    @OneToOne
    @MapsId
    @JoinColumn(name = "PK_DOCUMENT_ID")
    public User getUser() {
         return user;
     }
}

https://www.baeldung.com/jpa-one-to-one

参考资料

java - How use Hibernate and MyBatis in the same application - Stack Overflow

Spring混合框架(Hibernate+JDBC/iBatis)的事务管理_零度的博客专栏-CSDN博客

hibernate-orm/migration-guide.adoc at 5.0 · hibernate/hibernate-orm · GitHub

其它

Hibernate 5.3.0 is the first version that’s fully compliant with JPA 2.2. However, because the support for all the interesting features was already added in Hibernate 5.0, 5.1 and 5.2, and I already wrote extensive tutorials about all of them, I will not dive any deeper into this topic.

相关系列博客

Spring boot升级到2.3.2.Release和Spring framework升级到5.28.Release踩过的坑_keeppractice的博客-CSDN博客

Spring boot admin 升级到2.3.1 遇到的问题总结_keeppractice的博客-CSDN博客

错误总结]升级spring-boot->2.6.2|hiberate->5.4.33.Final|spring cloud->2021.0.0 |spring admin->2.4.1qs ​

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐