赞
踩
背景:有个项目中在内网和外网各部署了一套,且要将外网的数据摆渡到内网中入库;但是内网中的数据也会新增,类似双主双写操作,外网的主键使用奇数增长,内网主键使用偶数增长,保证内外网数据主键不冲突。
内网入库的发现,JPA主键是自增长的策略,即使id不为空,但是保存的数据后还是会使用数据库的自增长的id,未使用id值,导致两边的数据不一致。
期望:
使用自定义主键生成策略,继承原有主键自增长策略实现类org.hibernate.id.IdentityGenerator
,重新实现IdentifierGenerator#generate(SharedSessionContractImplementor session, Object object)
public class TestIdGenerator extends IdentityGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object obj) throws HibernateException {
if (obj instanceof Test) {
Test t = (Test) obj;
if (t.getId() != null) {//当id不空时,使用该id值作为主键
return new Assigned().generate(session,obj);
}
}
//当id == null 时 使用原因的自增长策略
return super.generate(session, obj);
}
}
使用方法
@Data
@Entity
public class Test {
@Id
// @GeneratedValue(strategy = GenerationType.IDENTITY)
@GeneratedValue(generator = "sq_id")
@GenericGenerator(name = "sq_id", strategy = "com.test.entity.TestIdGenerator")
private Integer id;
private String name;
}
由于JPA默认的实现是Hibernate,save()
方法最终会调用hibernate的AbstractSaveEventListener#saveWithGeneratedId( Object entity, String entityName, Object anything, EventSource source, boolean requiresImmediateIdAccess)
来完成入库。
saveWithGeneratedId()
源码:/** * Prepares the save call using a newly generated id. * * @param entity The entity to be saved * @param entityName The entity-name for the entity to be saved * @param anything Generally cascade-specific information. * @param source The session which is the source of this save event. * @param requiresImmediateIdAccess does the event context require * access to the identifier immediately after execution of this method (if * not, post-insert style id generators may be postponed if we are outside * a transaction). * * @return The id used to save the entity; may be null depending on the * type of id generator used and the requiresImmediateIdAccess value */ protected Serializable saveWithGeneratedId( Object entity, String entityName, Object anything, EventSource source, boolean requiresImmediateIdAccess) { callbackRegistry.preCreate( entity ); if ( entity instanceof SelfDirtinessTracker ) { ( (SelfDirtinessTracker) entity ).$$_hibernate_clearDirtyAttributes(); } EntityPersister persister = source.getEntityPersister( entityName, entity ); Serializable generatedId = persister.getIdentifierGenerator().generate( source, entity ); if ( generatedId == null ) { throw new IdentifierGenerationException( "null id generated for:" + entity.getClass() ); } else if ( generatedId == IdentifierGeneratorHelper.SHORT_CIRCUIT_INDICATOR ) { return source.getIdentifier( entity ); } else if ( generatedId == IdentifierGeneratorHelper.POST_INSERT_INDICATOR ) { return performSave( entity, null, persister, true, anything, source, requiresImmediateIdAccess ); } else { // TODO: define toString()s for generators if ( LOG.isDebugEnabled() ) { LOG.debugf( "Generated identifier: %s, using strategy: %s", persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ), persister.getIdentifierGenerator().getClass().getName() ); } return performSave( entity, generatedId, persister, false, anything, source, true ); } }
项目启动时会扫描所有的@Entity
标记的实体,转换成SingleTableEntityPersister
,该persister包含实体类的源数据信息以及对应的插入,修改、删除的sql语句模板信息
通过实体类的persister 获取主键生成器IdentifierGenerator
,通过generate()
方法来获取id。
generatedId
值则为IdentifierGeneratorHelper.POST_INSERT_INDICATOR
,则调用performSave( entity, null, persister, true, anything, source, requiresImmediateIdAccess )
进入后续方法。注意第二个参数和第四个参数,null 代表id值为空,true:表示id依赖数据库列id增长performSave(...)
方法。注意第二个参数和第四个参数,id值,false:id不需要依赖数据库列增长performSave()
方法源码见源码中的注释,最终执行performSaveOrReplicate
方法
protected Serializable performSave( Object entity, Serializable id, EntityPersister persister, boolean useIdentityColumn, Object anything, EventSource source, boolean requiresImmediateIdAccess) { final EntityKey key; //上面方面传入的第四参数,只有id不能空时,才会执行 if ( !useIdentityColumn ) { //判断当前系统中,实体类中设置的id值是否是唯一值 key = source.generateEntityKey( id, persister ); Object old = source.getPersistenceContext().getEntity( key ); if ( old != null ) { if ( source.getPersistenceContext().getEntry( old ).getStatus() == Status.DELETED ) { source.forceFlush( source.getPersistenceContext().getEntry( old ) ); } else { throw new NonUniqueObjectException( id, persister.getEntityName() ); } } //设置主键id的值 persister.setIdentifier( entity, id, source ); } else { key = null; } if ( invokeSaveLifecycle( entity, persister, source ) ) { return id; //EARLY EXIT } return performSaveOrReplicate( entity, key, persister, useIdentityColumn, anything, source, requiresImmediateIdAccess ); }
performSaveOrReplicate()
方法源码见源码中的注释。最核心的代码是addInsertAction()
方法,该方法是对自增长和其他主键策略生成实际SQL执行类
protected Serializable performSaveOrReplicate( Object entity, EntityKey key, EntityPersister persister, boolean useIdentityColumn, Object anything, EventSource source, boolean requiresImmediateIdAccess) { Serializable id = key == null ? null : key.getIdentifier(); boolean inTrx = source.isTransactionInProgress(); boolean shouldDelayIdentityInserts = !inTrx && !requiresImmediateIdAccess; // 省略部分无关的代码。。。。。 //核心代码,对自增长和其他主键策略生成实际SQL执行类 AbstractEntityInsertAction insert = addInsertAction( values, id, entity, persister, useIdentityColumn, source, shouldDelayIdentityInserts ); // 省略部分无关核心的代码。。。。。 return id; }
addInsertAction()
源码EntityIdentityInsertAction
Sql执行类对象EntityInsertAction
Sql执行类对象通过addAction(insert)
方法,来调用Executable#execute
方法,由于EntityIdentityInsertAction
和EntityInsertAction
都实现了该方法,实际调用的就是这类下的execute()
方法
private AbstractEntityInsertAction addInsertAction( Object[] values, Serializable id, Object entity, EntityPersister persister, boolean useIdentityColumn, EventSource source, boolean shouldDelayIdentityInserts) { if ( useIdentityColumn ) { EntityIdentityInsertAction insert = new EntityIdentityInsertAction( values, entity, persister, isVersionIncrementDisabled(), source, shouldDelayIdentityInserts ); source.getActionQueue().addAction( insert ); return insert; } else { Object version = Versioning.getVersion( values, persister ); EntityInsertAction insert = new EntityInsertAction( id, values, entity, version, persister, isVersionIncrementDisabled(), source ); source.getActionQueue().addAction( insert ); return insert; } }
EntityIdentityInsertAction #execute()
自增长策略执行SQL源码@Override public void execute() throws HibernateException { nullifyTransientReferencesIfNotAlready(); final EntityPersister persister = getPersister(); final SharedSessionContractImplementor session = getSession(); final Object instance = getInstance(); // 省略部分无关的代码。。。。。 if ( !isVeto() ) { //插入数据 generatedId = persister.insert( getState(), instance, session ); if ( persister.hasInsertGeneratedProperties() ) { persister.processInsertGeneratedProperties( generatedId, instance, getState(), session ); } //need to do that here rather than in the save event listener to let //the post insert events to have a id-filled entity when IDENTITY is used (EJB3) persister.setIdentifier( instance, generatedId, session ); session.getPersistenceContext().registerInsertedKey( getPersister(), generatedId ); entityKey = session.generateEntityKey( generatedId, persister ); session.getPersistenceContext().checkUniqueness( entityKey, getInstance() ); } // 省略部分无关的代码。。。。。 }
调用3.1 中的persister 的insert(fields, object, session)
来实现插入。insert 实现如下。使用getSQLIdentityInsertString()
来获取插入执行sql语句
public Serializable insert(Object[] fields, Object object, SharedSessionContractImplementor session) throws HibernateException { // apply any pre-insert in-memory value generation preInsertInMemoryValueGeneration( fields, object, session ); final int span = getTableSpan(); final Serializable id; if ( entityMetamodel.isDynamicInsert() ) { //动态插入,这边用不到 ..... } else { // 使用静态模板插入。getSQLIdentityInsertString() 获取静态插入模板 persister.sqlIdentityInsertString 字段值 id = insert( fields, getPropertyInsertability(), getSQLIdentityInsertString(), object, session ); ....... } return id; }
EntityInsertAction #execute()
其他策略SQL执行源码@Override public void execute() throws HibernateException { nullifyTransientReferencesIfNotAlready(); final EntityPersister persister = getPersister(); final SharedSessionContractImplementor session = getSession(); final Object instance = getInstance(); final Serializable id = getId(); final boolean veto = preInsert(); //....... if ( !veto ) { //插入数据 persister.insert( id, getState(), instance, session ); PersistenceContext persistenceContext = session.getPersistenceContext(); final EntityEntry entry = persistenceContext.getEntry( instance ); if ( entry == null ) { throw new AssertionFailure( "possible non-threadsafe access to session" ); } entry.postInsert( getState() ); ...... } ........ }
调用3.1 中的persister 的insert(id, fields,object, session)
来实现插入。insert 实现如下,使用getSQLInsertStrings()[j]
来获取插入执行sql语句
public void insert(Serializable id, Object[] fields, Object object, SharedSessionContractImplementor session) {
// apply any pre-insert in-memory value generation
preInsertInMemoryValueGeneration( fields, object, session );
final int span = getTableSpan();
if ( entityMetamodel.isDynamicInsert() ) {
.......
}
else {
// For the case of dynamic-insert="false", use the static SQL
for ( int j = 0; j < span; j++ ) {
insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。