Hibernate 自定义主键生成策略与源码浅析

这是一个矛盾的设计:

为什么说是失败的设计: 因为不支持友好扩展,死代码就是死代码,不能 删除后手动恢复。
但是矛盾点在于,我如果修改了唯一值的时候,会找不到对应的值,但是可以手动添加。

使用场景:

这些自动生成策略不支持的情况下:
AbstractPostInsertGenerator
Assigned
CompositeNestedGeneratedValueGenerator
ForeignGenerator
GUIDGenerator
IncrementGenerator
MultipleHiLoPerTableGenerator
MyUUIDGenerator
SequenceGenerator
SequenceHiLoGenerator
SequenceIdentityGenerator
SequenceStyleGenerator
TableGenerator
UUIDGenerator
UUIDHexGenerator

比如,您想写死几个id,又想不想写死的自动生成,这个矛盾的思路,就需要您自己手动创建策略

为什么写死的id,会被替换(源码解析)

首先列举一下正常的保存思路:

调用org.springframework.data.jpa.repository.support  SimpleJpaRepository 的 save 方法:
saveflush同理:

因为:
Hibernate 自定义主键生成策略与源码浅析

然后:咱们开始分析 save 方法的源码:
Hibernate 自定义主键生成策略与源码浅析

this.entityInformation.isNew(entity):

这个:this.versionAttribute 需要您看一下是什么时候赋值的往上查,可以看到这个应该是
Hibernate 自定义主键生成策略与源码浅析
this.versionAttribute 为空后:判断id的class 是否是引用类型,如果不是引用类型就抛异常,如果是,就继续判断值是否为空并把结果传到最开始的流程
Hibernate 自定义主键生成策略与源码浅析
Hibernate 自定义主键生成策略与源码浅析
3.因为id==null–》false 走到这个流程

Hibernate 自定义主键生成策略与源码浅析

Hibernate 自定义主键生成策略与源码浅析
走到了AbstractEntityManagerImpl checkOpen 就是判断 EntityManager Session 等是否存活
Hibernate 自定义主键生成策略与源码浅析

Hibernate 自定义主键生成策略与源码浅析
Hibernate 自定义主键生成策略与源码浅析

Hibernate 自定义主键生成策略与源码浅析

Hibernate 自定义主键生成策略与源码浅析
return this.internalGetSession().merge(entity);


    public void onMerge(MergeEvent event, Map copiedAlready) throws HibernateException {
        MergeContext copyCache = (MergeContext)copiedAlready;
        EventSource source = event.getSession();
        Object original = event.getOriginal();
        if (original != null) {
            Object entity;
            if (original instanceof HibernateProxy) {
                LazyInitializer li = ((HibernateProxy)original).getHibernateLazyInitializer();
                if (li.isUninitialized()) {
                    LOG.trace("Ignoring uninitialized proxy");
                    event.setResult(source.load(li.getEntityName(), li.getIdentifier()));
                    return;
                }

                entity = li.getImplementation();
            } else {
                entity = original;
            }

            if (copyCache.containsKey(entity) && copyCache.isOperatedOn(entity)) {
                LOG.trace("Already in merge process");
                event.setResult(entity);
            } else {
                if (copyCache.containsKey(entity)) {
                    LOG.trace("Already in copyCache; setting in merge process");
                    copyCache.setOperatedOn(entity, true);
                }

                event.setEntity(entity);
                EntityState entityState = null;
                EntityEntry entry = source.getPersistenceContext().getEntry(entity);
                if (entry == null) {
                    EntityPersister persister = source.getEntityPersister(event.getEntityName(), entity);
                    Serializable id = persister.getIdentifier(entity, source);
                    if (id != null) {
                        EntityKey key = source.generateEntityKey(id, persister);
                        Object managedEntity = source.getPersistenceContext().getEntity(key);
                        entry = source.getPersistenceContext().getEntry(managedEntity);
                        if (entry != null) {
                            entityState = EntityState.DETACHED;
                        }
                    }
                }

                if (entityState == null) {
                    entityState = this.getEntityState(entity, event.getEntityName(), entry, source);
                }

                switch(entityState) {
                case DETACHED:
                    this.entityIsDetached(event, copyCache);
                    break;
                case TRANSIENT:
                    this.entityIsTransient(event, copyCache);
                    break;
                case PERSISTENT:
                    this.entityIsPersistent(event, copyCache);
                    break;
                default:
                    throw new ObjectDeletedException("deleted instance passed to merge", (Serializable)null, this.getLoggableName(event.getEntityName(), entity));
                }
            }
        }

    }

Hibernate 自定义主键生成策略与源码浅析
如果数据库有,就走更新,把查出来的实体,和现在的实体进行对比,合并

之后的合并全是,copyCache里的两个实体的merge
Hibernate 自定义主键生成策略与源码浅析
this.copyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.FROM_PARENT);
给value 赋值 其他对象属性

Hibernate 自定义主键生成策略与源码浅析
Hibernate 自定义主键生成策略与源码浅析
我本以为源码中会判读两个值,但是并没有,自动生成主键规则调用生成主键,根本就不会在比较,直接就把id给改了:所以破局的原因就是主键的生成策略

this.idSetter : 可以研究一下 我会等有空在研究一下,目前看是绑定的规则的时候赋值的
Hibernate 自定义主键生成策略与源码浅析

策略代码:

package com.adc.da.util.utils;

import org.hibernate.*;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.UUIDGenerationStrategy;
import org.hibernate.id.uuid.StandardRandomStrategy;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;
import org.springframework.orm.hibernate4.support.HibernateDaoSupport;

import java.io.Serializable;
import java.util.Properties;
import java.util.UUID;

public class MyUUIDGenerator
        extends HibernateDaoSupport implements IdentifierGenerator, Configurable {

    private static final CoreMessageLogger LOG = CoreLogging.messageLogger(MyUUIDGenerator.class);

    /**
     * 实体名称
     */
    private String entityName;

    public static final String UUID_GEN_STRATEGY = "uuid_gen_strategy";
    public static final String UUID_GEN_STRATEGY_CLASS = "uuid_gen_strategy_class";
    private UUIDGenerationStrategy strategy;
    private UUIDTypeDescriptor.ValueTransformer valueTransformer;

    /**
     * @param session
     * @param obj     传过来的实体
     * @return
     * @throws HibernateException
     */
    @Override
    public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {

        Serializable id = session.getEntityPersister(this.entityName, obj).getIdentifier(obj, session);

        if (id == null) {
            return this.valueTransformer.transform(this.strategy.generateUUID(session));
        } else {
            SessionFactory sessionFactory = session.getFactory();
            Session session2 = sessionFactory.openSession();
            Transaction transaction = session2.beginTransaction();
            try {

                transaction.begin();

                Object o = session2.get(Class.forName(entityName), id.toString());
                if (o != null) {
                    return UUID.randomUUID().toString();
                } else {
                    return id;
                }

            } catch (ClassNotFoundException e) {
                LOG.error(e.getMessage());
            } finally {
                transaction.commit();
                session2.close();
            }


        }


        return null;
    }

    @Override
    public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
        this.entityName = params.getProperty("entity_name");
        if (this.entityName == null) {
            throw new MappingException("no entity name");
        }

        this.strategy = (UUIDGenerationStrategy) params.get("uuid_gen_strategy");
        if (this.strategy == null) {
            String strategyClassName = params.getProperty("uuid_gen_strategy_class");
            if (strategyClassName != null) {
                try {
                    ClassLoaderService cls = (ClassLoaderService) serviceRegistry.getService(ClassLoaderService.class);
                    Class strategyClass = cls.classForName(strategyClassName);

                    try {
                        this.strategy = (UUIDGenerationStrategy) strategyClass.newInstance();
                    } catch (Exception var8) {
                        LOG.unableToInstantiateUuidGenerationStrategy(var8);
                    }
                } catch (ClassLoadingException var9) {
                    LOG.unableToLocateUuidGenerationStrategy(strategyClassName);
                }
            }
        }

        if (this.strategy == null) {
            this.strategy = StandardRandomStrategy.INSTANCE;
        }

        if (UUID.class.isAssignableFrom(type.getReturnedClass())) {
            this.valueTransformer = UUIDTypeDescriptor.PassThroughTransformer.INSTANCE;
        } else if (String.class.isAssignableFrom(type.getReturnedClass())) {
            this.valueTransformer = UUIDTypeDescriptor.ToStringTransformer.INSTANCE;
        } else {
            if (!byte[].class.isAssignableFrom(type.getReturnedClass())) {
                throw new HibernateException("Unanticipated return type [" + type.getReturnedClass().getName() + "] for UUID conversion");
            }

            this.valueTransformer = UUIDTypeDescriptor.ToBytesTransformer.INSTANCE;
        }
    }
}