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同理:

因爲:
saveAndFlush 調用步驟

然後:咱們開始分析 save 方法的源碼:
在這裏插入圖片描述

this.entityInformation.isNew(entity):

這個:this.versionAttribute 需要您看一下是什麼時候賦值的往上查,可以看到這個應該是
在這裏插入圖片描述
this.versionAttribute 爲空後:判斷id的class 是否是引用類型,如果不是引用類型就拋異常,如果是,就繼續判斷值是否爲空並把結果傳到最開始的流程
在這裏插入圖片描述
在這裏插入圖片描述
3.因爲id==null–》false 走到這個流程

在這裏插入圖片描述

在這裏插入圖片描述
走到了AbstractEntityManagerImpl checkOpen 就是判斷 EntityManager Session 等是否存活
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述
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));
                }
            }
        }

    }

在這裏插入圖片描述
如果數據庫有,就走更新,把查出來的實體,和現在的實體進行對比,合併

之後的合併全是,copyCache裏的兩個實體的merge
在這裏插入圖片描述
this.copyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.FROM_PARENT);
給value 賦值 其他對象屬性

在這裏插入圖片描述
在這裏插入圖片描述
我本以爲源碼中會判讀兩個值,但是並沒有,自動生成主鍵規則調用生成主鍵,根本就不會在比較,直接就把id給改了:所以破局的原因就是主鍵的生成策略

this.idSetter : 可以研究一下 我會等有空在研究一下,目前看是綁定的規則的時候賦值的
在這裏插入圖片描述

策略代碼:

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;
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章