使hibernate
在读取懒加载属性且不在事务中不会抛出LazyInitializationException
,而且返回null
hibernate
ToOne(@ManyToOne、@OneToOne)
使之抛出异常是ByteBuddyInterceptor
引起的,而ByteBuddyInterceptor
是由ByteBuddyProxyFactory
构建的,ByteBuddyProxyFactory
是由ProxyFactoryFactoryImpl
构建的,ByteBuddyProxyFactory
是由BytecodeProviderImpl
构建的,所以重写这些类
重写BytecodeProviderImpl
:
public class KuKuBytecodeProvider extends BytecodeProviderImpl {
@Override
public ProxyFactoryFactory getProxyFactoryFactory() {
try {
Class<BytecodeProviderImpl> clazz = BytecodeProviderImpl.class;
Field stateField = clazz.getDeclaredField("byteBuddyState");
stateField.setAccessible(true);
ByteBuddyState byteBuddyState = (ByteBuddyState) stateField.get(this);
Field helperField = clazz.getDeclaredField("byteBuddyProxyHelper");
helperField.setAccessible(true);
ByteBuddyProxyHelper byteBuddyProxyHelper = (ByteBuddyProxyHelper) helperField.get(this);
return new KuKuProxyFactoryFactory(byteBuddyState, byteBuddyProxyHelper);
} catch (Exception e) {
return null;
}
}
}
重写ProxyFactoryFactoryImpl
public class KuKuProxyFactoryFactory extends ProxyFactoryFactoryImpl {
public KuKuProxyFactoryFactory(ByteBuddyState byteBuddyState, ByteBuddyProxyHelper byteBuddyProxyHelper) {
super(byteBuddyState, byteBuddyProxyHelper);
}
@Override
public ProxyFactory buildProxyFactory(SessionFactoryImplementor sessionFactory) {
try {
Class<ProxyFactoryFactoryImpl> clazz = ProxyFactoryFactoryImpl.class;
Field field = clazz.getDeclaredField("byteBuddyProxyHelper");
field.setAccessible(true);
ByteBuddyProxyHelper byteBuddyProxyHelper = (ByteBuddyProxyHelper) field.get(this);
return new KuKuByteBuddyProxyFactory(byteBuddyProxyHelper);
} catch (Exception e) {
return null;
}
}
}
重写ByteBuddyProxyFactory
public class KuKuByteBuddyProxyFactory extends ByteBuddyProxyFactory {
private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyFactory.class );
public KuKuByteBuddyProxyFactory(ByteBuddyProxyHelper byteBuddyProxyHelper) {
super(byteBuddyProxyHelper);
}
@Override
public HibernateProxy getProxy(Serializable id, SharedSessionContractImplementor session) throws HibernateException {
try {
Class<ByteBuddyProxyFactory> clazz = ByteBuddyProxyFactory.class;
Field entityNameField = clazz.getDeclaredField("entityName");
entityNameField.setAccessible(true);
String entityName = (String) entityNameField.get(this);
Field persistentClassField = clazz.getDeclaredField("persistentClass");
persistentClassField.setAccessible(true);
Class persistentClass = (Class) persistentClassField.get(this);
Field interfacesField = clazz.getDeclaredField("interfaces");
interfacesField.setAccessible(true);
Class[] interfaces = (Class[]) interfacesField.get(this);
Field getIdentifierMethodField = clazz.getDeclaredField("getIdentifierMethod");
getIdentifierMethodField.setAccessible(true);
Method getIdentifierMethod = (Method) getIdentifierMethodField.get(this);
Field setIdentifierMethodField = clazz.getDeclaredField("setIdentifierMethod");
setIdentifierMethodField.setAccessible(true);
Method setIdentifierMethod = (Method) setIdentifierMethodField.get(this);
Field componentIdTypeField = clazz.getDeclaredField("componentIdType");
componentIdTypeField.setAccessible(true);
CompositeType componentIdType = (CompositeType) componentIdTypeField.get(this);
Field overridesEqualsField = clazz.getDeclaredField("overridesEquals");
overridesEqualsField.setAccessible(true);
boolean overridesEquals = (boolean) overridesEqualsField.get(this);
Field proxyClassField = clazz.getDeclaredField("proxyClass");
proxyClassField.setAccessible(true);
Class proxyClass = (Class) proxyClassField.get(this);
final ByteBuddyInterceptor interceptor = new KuKuByteBuddyInterceptor(
entityName,
persistentClass,
interfaces,
id,
getIdentifierMethod,
setIdentifierMethod,
componentIdType,
session,
overridesEquals
);
try {
final HibernateProxy proxy = (HibernateProxy) proxyClass.getConstructor().newInstance();
( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor );
return proxy;
}
catch (NoSuchMethodException e) {
String logMessage = LOG.bytecodeEnhancementFailedBecauseOfDefaultConstructor(entityName);
LOG.error( logMessage, e );
throw new HibernateException( logMessage, e );
}
catch (Throwable t) {
String logMessage = LOG.bytecodeEnhancementFailed(entityName);
LOG.error( logMessage, t );
throw new HibernateException( logMessage, t );
}
} catch (Exception e) {
return null;
}
}
}
public class KuKuByteBuddyProxyFactory extends ByteBuddyProxyFactory {
public KuKuByteBuddyProxyFactory(ByteBuddyProxyHelper byteBuddyProxyHelper) {
super(byteBuddyProxyHelper);
}
@Override
public HibernateProxy getProxy(Object id, SharedSessionContractImplementor session) throws HibernateException {
try {
Class<ByteBuddyProxyFactory> clazz = ByteBuddyProxyFactory.class;
Field entityNameField = clazz.getDeclaredField("entityName");
entityNameField.setAccessible(true);
String entityName = (String) entityNameField.get(this);
Field persistentClassField = clazz.getDeclaredField("persistentClass");
persistentClassField.setAccessible(true);
Class persistentClass = (Class) persistentClassField.get(this);
Field interfacesField = clazz.getDeclaredField("interfaces");
interfacesField.setAccessible(true);
Class[] interfaces = (Class[]) interfacesField.get(this);
Field getIdentifierMethodField = clazz.getDeclaredField("getIdentifierMethod");
getIdentifierMethodField.setAccessible(true);
Method getIdentifierMethod = (Method) getIdentifierMethodField.get(this);
Field setIdentifierMethodField = clazz.getDeclaredField("setIdentifierMethod");
setIdentifierMethodField.setAccessible(true);
Method setIdentifierMethod = (Method) setIdentifierMethodField.get(this);
Field componentIdTypeField = clazz.getDeclaredField("componentIdType");
componentIdTypeField.setAccessible(true);
CompositeType componentIdType = (CompositeType) componentIdTypeField.get(this);
Field overridesEqualsField = clazz.getDeclaredField("overridesEquals");
overridesEqualsField.setAccessible(true);
boolean overridesEquals = (boolean) overridesEqualsField.get(this);
final ByteBuddyInterceptor interceptor = new KuKuByteBuddyInterceptor(
entityName,
persistentClass,
interfaces,
id,
getIdentifierMethod,
setIdentifierMethod,
componentIdType,
session,
overridesEquals
);
Method proxyMethod = clazz.getDeclaredMethod("getHibernateProxy");
final HibernateProxy instance = (HibernateProxy) proxyMethod.invoke(this);
final ProxyConfiguration proxyConfiguration = instance.asProxyConfiguration();
if ( proxyConfiguration == null ) {
throw new HibernateException( "Produced proxy does not correctly implement ProxyConfiguration" );
}
proxyConfiguration.$$_hibernate_set_interceptor( interceptor );
return instance;
} catch (Exception e) {
return null;
}
}
}
重写ByteBuddyInterceptor
public class KuKuByteBuddyInterceptor extends ByteBuddyInterceptor {
public KuKuByteBuddyInterceptor(String entityName, Class persistentClass, Class[] interfaces, Serializable id, Method getIdentifierMethod, Method setIdentifierMethod, CompositeType componentIdType, SharedSessionContractImplementor session, boolean overridesEquals) {
super(entityName, persistentClass, interfaces, id, getIdentifierMethod, setIdentifierMethod, componentIdType, session, overridesEquals);
}
@Override
public Object intercept(Object proxy, Method thisMethod, Object[] args) throws Throwable {
try {
return super.intercept(proxy, thisMethod, args);
} catch (Exception e) {
return null;
}
}
}
替换Provider
jdk8
BytecodeProviderImpl
是在Environment
生成的一个常量,所以在开始替换掉BytecodeProviderImpl
为我们重写的
try {
Class<Environment> clazz = Environment.class;
Field field = clazz.getDeclaredField("BYTECODE_PROVIDER_INSTANCE");
field.setAccessible(true);
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);//fianl标志位置0
field.set(null, new KuKuBytecodeProvider());
} catch (Exception e) {
e.printStackTrace();
}
jdk17
jdk17限制了反射,获取不到Field的属性,所以使用另外的方法,方法来自 https://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection
try {
val clazz = Environment::class.java
val field = clazz.getDeclaredField("BYTECODE_PROVIDER_INSTANCE")
field.isAccessible = true
val lookup = MethodHandles.privateLookupIn(Field::class.java, MethodHandles.lookup())
// val handle = lookup.findVarHandle(Field::class.java, "modifiers", Int::class.java)
val setter = lookup.findSetter(Field::class.java, "modifiers", Int::class.java)
setter.invoke(field, field.modifiers and Modifier.FINAL.inv())
// handle.set(field, field.modifiers and Modifier.FINAL.inv())
field.set(null, KuKuBytecodeProvider())
} catch (e: Exception) {
e.printStackTrace()
}
添加vm启动参数--add-opens java.base/java.lang.reflect=ALL-UNNAMED
ToMany(@OneToMany、@ManyToMany)
ToMany
的话是一个集合,这个Hibernate
提供自定义集合类,我一般都是使用Set
,所以以Set
为例子
Hibernate
对Set
提供的类是PersistentSet
,在其执行read
方法时,如果是懒加载(ToMany注解默认为懒加载)且不在事务中,会抛出LazyInitializationException
异常,但是read
方法及其内部真正抛出异常的方法都是final
,不能被重写,那就只能重写其调用了read
方法的方法了
继承于PersistentSet
,重写其方法,在其重写Set
方法中处理异常,并返回空Set
对应的方法
class KuKuPersistentSet<E>: PersistentSet<E> {
constructor(session: SharedSessionContractImplementor): super(session)
constructor(session: SharedSessionContractImplementor, set: Set<E>): super(session, set)
override fun iterator(): MutableIterator<E> {
return try {
super.iterator()
} catch (e: Exception) {
mutableSetOf<E>().iterator()
}
}
override fun toArray(): Array<Any> {
return try {
super.toArray()
} catch (e: Exception) {
hashSetOf<Any>().toArray()
}
}
override fun <A : Any?> toArray(array: Array<out A>): Array<A> {
return try {
super.toArray(array)
} catch (e: Exception) {
hashSetOf<A>().toArray(array)
}
}
override fun containsAll(elements: Collection<E>): Boolean {
return try {
super.containsAll(elements)
} catch (e: Exception) {
false
}
}
override fun toString(): String {
return try {
super.toString()
} catch (e: Exception) {
mutableSetOf<E>().toString()
}
}
override fun equals(other: Any?): Boolean {
return try {
super.equals(other)
} catch (e: Exception) {
false
}
}
override fun hashCode(): Int {
return try {
super.hashCode()
} catch (e: Exception) {
0
}
}
然后写一个CollectionType
,实现接口UserCollectionType
,并使用重写的PersistentSet
class KuKuSetType: UserCollectionType{
override fun getClassification(): CollectionClassification {
return CollectionClassification.SET
}
override fun getCollectionClass(): Class<*> {
return Set::class.java
}
override fun instantiate(
session: SharedSessionContractImplementor,
persister: CollectionPersister
): PersistentCollection<*> {
return KuKuPersistentSet<Any>(session)
}
override fun instantiate(anticipatedSize: Int): Any {
return mutableSetOf<Any>()
}
override fun wrap(session: SharedSessionContractImplementor, collection: Any): PersistentCollection<*> {
return KuKuPersistentSet<Any>(session, collection as Set<Any>)
}
override fun getElementsIterator(collection: Any): MutableIterator<*> {
val set = collection as MutableSet<*>
return set.iterator()
}
override fun contains(collection: Any, entity: Any): Boolean {
val set = collection as MutableSet<*>
return set.contains(entity)
}
override fun indexOf(collection: Any?, entity: Any?): Any {
val set = collection as MutableSet<*>
return set.indexOf(entity)
}
override fun replaceElements(
original: Any?,
target: Any?,
persister: CollectionPersister?,
owner: Any?,
copyCache: MutableMap<Any?, Any?>?,
session: SharedSessionContractImplementor?
): Any {
val set = target as MutableSet<Any>
set.clear()
set.addAll(original as Set<out Any>)
return set
}
}
在对应的ToMany
属性上使用自定义的CollectionType
@OneToMany(mappedBy = "byLazy")
@CollectionType(type = KuKuSetType::class)
var lazy: Set<LazyEntity> = mutableSetOf()
使用jackson模块
当然,在json
序列化的时候这种问题也可以使用jackson
的一个模块来实现,这个模块会把懒加载的全部序列化为null
// jakarta包的hibernate
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5-jakarta:2.14.1")
// javax包的hibernate
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:2.14.1")
如果为spring,直接把模块加入到容器中
@Component
class RegisterConfig {
@Bean
fun ss(): Hibernate5JakartaModule {
return Hibernate5JakartaModule()
}
}