介绍
既然您了解了Hibernate脏检查的基础知识 ,我们就可以研究增强的脏检查机制。 虽然默认的图遍历算法对于大多数用例可能已足够,但有时您需要优化的脏检查算法,并且检测方法比构建自己的自定义策略更方便。
使用Ant休眠工具
传统上,Hibernate Tools一直专注于Ant和Eclipse。 从Hibernate 3开始就可以进行字节码检测,但是它需要Ant任务才能运行CGLIB或Javassist字节码增强例程。
Maven支持通过maven-antrun-plugin运行Ant任务:
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>Instrument domain classes</id>
<configuration>
<tasks>
<taskdef name="instrument"
classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath>
<path refid="maven.dependency.classpath"/>
<path refid="maven.plugin.classpath"/>
</classpath>
</taskdef>
<instrument verbose="true">
<fileset dir="${project.build.outputDirectory}">
<include name="**/flushing/*.class"/>
</fileset>
</instrument>
</tasks>
</configuration>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>${javassist.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
因此,对于以下实体源类:
@Entity
public class EnhancedOrderLine {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long number;
private String orderedBy;
private Date orderedOn;
public Long getId() {
return id;
}
public Long getNumber() {
return number;
}
public void setNumber(Long number) {
this.number = number;
}
public String getOrderedBy() {
return orderedBy;
}
public void setOrderedBy(String orderedBy) {
this.orderedBy = orderedBy;
}
public Date getOrderedOn() {
return orderedOn;
}
public void setOrderedOn(Date orderedOn) {
this.orderedOn = orderedOn;
}
}
在构建期间,将生成以下类:
@Entity
public class EnhancedOrderLine implements FieldHandled {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private Long number;
private String orderedBy;
private Date orderedOn;
private transient FieldHandler $JAVASSIST_READ_WRITE_HANDLER;
public Long getId() {
return $javassist_read_id();
}
public Long getNumber() {
return $javassist_read_number();
}
public void setNumber(Long number) {
$javassist_write_number(number);
}
public String getOrderedBy() {
return $javassist_read_orderedBy();
}
public void setOrderedBy(String orderedBy) {
$javassist_write_orderedBy(orderedBy);
}
public Date getOrderedOn() {
return $javassist_read_orderedOn();
}
public void setOrderedOn(Date orderedOn) {
$javassist_write_orderedOn(orderedOn);
}
public FieldHandler getFieldHandler() {
return this.$JAVASSIST_READ_WRITE_HANDLER;
}
public void setFieldHandler(FieldHandler paramFieldHandler) {
this.$JAVASSIST_READ_WRITE_HANDLER = paramFieldHandler;
}
public Long $javassist_read_id() {
if (getFieldHandler() == null)
return this.id;
}
public void $javassist_write_id(Long paramLong) {
if (getFieldHandler() == null) {
this.id = paramLong;
return;
}
this.id = ((Long)getFieldHandler().writeObject(this, "id", this.id, paramLong));
}
public Long $javassist_read_number() {
if (getFieldHandler() == null)
return this.number;
}
public void $javassist_write_number(Long paramLong) {
if (getFieldHandler() == null) {
this.number = paramLong;
return;
}
this.number = ((Long)getFieldHandler().writeObject(this, "number", this.number, paramLong));
}
public String $javassist_read_orderedBy() {
if (getFieldHandler() == null)
return this.orderedBy;
}
public void $javassist_write_orderedBy(String paramString) {
if (getFieldHandler() == null) {
this.orderedBy = paramString;
return;
}
this.orderedBy = ((String)getFieldHandler().writeObject(this, "orderedBy", this.orderedBy, paramString));
}
public Date $javassist_read_orderedOn() {
if (getFieldHandler() == null)
return this.orderedOn;
}
public void $javassist_write_orderedOn(Date paramDate) {
if (getFieldHandler() == null) {
this.orderedOn = paramDate;
return;
}
this.orderedOn = ((Date)getFieldHandler().writeObject(this, "orderedOn", this.orderedOn, paramDate));
}
}
尽管org.hibernate.bytecode.instrumentation.spi.AbstractFieldInterceptor设法拦截脏字段,但在进行脏污跟踪时从未真正询问过此信息。
InstrumentTask字节码增强功能只能分辨一个实体是否脏了,缺乏对指示已修改哪些属性的支持,因此使InstrumentTask更适合“ No-proxy” LAZY提取策略 。
休眠增强Maven插件
Hibernate 4.2.8增加了对专用Maven字节码增强插件的支持 。
Maven字节码增强插件易于配置:
<build>
<plugins>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在项目构建期间,将生成以下类:
@Entity
public class EnhancedOrderLine
implements ManagedEntity, PersistentAttributeInterceptable, SelfDirtinessTracker {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long number;
private String orderedBy;
private Date orderedOn;
@Transient
private transient PersistentAttributeInterceptor $$_hibernate_attributeInterceptor;
@Transient
private transient Set $$_hibernate_tracker;
@Transient
private transient CollectionTracker $$_hibernate_collectionTracker;
@Transient
private transient EntityEntry $$_hibernate_entityEntryHolder;
@Transient
private transient ManagedEntity $$_hibernate_previousManagedEntity;
@Transient
private transient ManagedEntity $$_hibernate_nextManagedEntity;
public Long getId() {
return $$_hibernate_read_id();
}
public Long getNumber() {
return $$_hibernate_read_number();
}
public void setNumber(Long number) {
$$_hibernate_write_number(number);
}
public String getOrderedBy() {
return $$_hibernate_read_orderedBy();
}
public void setOrderedBy(String orderedBy) {
$$_hibernate_write_orderedBy(orderedBy);
}
public Date getOrderedOn() {
return $$_hibernate_read_orderedOn();
}
public void setOrderedOn(Date orderedOn) {
$$_hibernate_write_orderedOn(orderedOn);
}
public PersistentAttributeInterceptor $$_hibernate_getInterceptor() {
return this.$$_hibernate_attributeInterceptor;
}
public void $$_hibernate_setInterceptor(PersistentAttributeInterceptor paramPersistentAttributeInterceptor) {
this.$$_hibernate_attributeInterceptor = paramPersistentAttributeInterceptor;
}
public void $$_hibernate_trackChange(String paramString) {
if (this.$$_hibernate_tracker == null)
this.$$_hibernate_tracker = new HashSet();
if (!this.$$_hibernate_tracker.contains(paramString))
this.$$_hibernate_tracker.add(paramString);
}
private boolean $$_hibernate_areCollectionFieldsDirty() {
return ($$_hibernate_getInterceptor() != null) && (this.$$_hibernate_collectionTracker != null);
}
private void $$_hibernate_getCollectionFieldDirtyNames(Set paramSet) {
if (this.$$_hibernate_collectionTracker == null)
return;
}
public boolean $$_hibernate_hasDirtyAttributes() {
return ((this.$$_hibernate_tracker == null) || (this.$$_hibernate_tracker.isEmpty())) && ($$_hibernate_areCollectionFieldsDirty());
}
private void $$_hibernate_clearDirtyCollectionNames() {
if (this.$$_hibernate_collectionTracker == null)
this.$$_hibernate_collectionTracker = new CollectionTracker();
}
public void $$_hibernate_clearDirtyAttributes() {
if (this.$$_hibernate_tracker != null)
this.$$_hibernate_tracker.clear();
$$_hibernate_clearDirtyCollectionNames();
}
public Set<String> $$_hibernate_getDirtyAttributes() {
if (this.$$_hibernate_tracker == null)
this.$$_hibernate_tracker = new HashSet();
$$_hibernate_getCollectionFieldDirtyNames(this.$$_hibernate_tracker);
return this.$$_hibernate_tracker;
}
private Long $$_hibernate_read_id() {
if ($$_hibernate_getInterceptor() != null)
this.id = ((Long) $$_hibernate_getInterceptor().readObject(this, "id", this.id));
return this.id;
}
private void $$_hibernate_write_id(Long paramLong) {
if (($$_hibernate_getInterceptor() == null) || ((this.id == null) || (this.id.equals(paramLong))))
break label39;
$$_hibernate_trackChange("id");
label39:
Long localLong = paramLong;
if ($$_hibernate_getInterceptor() != null)
localLong = (Long) $$_hibernate_getInterceptor().writeObject(this, "id", this.id, paramLong);
this.id = localLong;
}
private Long $$_hibernate_read_number() {
if ($$_hibernate_getInterceptor() != null)
this.number = ((Long) $$_hibernate_getInterceptor().readObject(this, "number", this.number));
return this.number;
}
private void $$_hibernate_write_number(Long paramLong) {
if (($$_hibernate_getInterceptor() == null) || ((this.number == null) || (this.number.equals(paramLong))))
break label39;
$$_hibernate_trackChange("number");
label39:
Long localLong = paramLong;
if ($$_hibernate_getInterceptor() != null)
localLong = (Long) $$_hibernate_getInterceptor().writeObject(this, "number", this.number, paramLong);
this.number = localLong;
}
private String $$_hibernate_read_orderedBy() {
if ($$_hibernate_getInterceptor() != null)
this.orderedBy = ((String) $$_hibernate_getInterceptor().readObject(this, "orderedBy", this.orderedBy));
return this.orderedBy;
}
private void $$_hibernate_write_orderedBy(String paramString) {
if (($$_hibernate_getInterceptor() == null) || ((this.orderedBy == null) || (this.orderedBy.equals(paramString))))
break label39;
$$_hibernate_trackChange("orderedBy");
label39:
String str = paramString;
if ($$_hibernate_getInterceptor() != null)
str = (String) $$_hibernate_getInterceptor().writeObject(this, "orderedBy", this.orderedBy, paramString);
this.orderedBy = str;
}
private Date $$_hibernate_read_orderedOn() {
if ($$_hibernate_getInterceptor() != null)
this.orderedOn = ((Date) $$_hibernate_getInterceptor().readObject(this, "orderedOn", this.orderedOn));
return this.orderedOn;
}
private void $$_hibernate_write_orderedOn(Date paramDate) {
if (($$_hibernate_getInterceptor() == null) || ((this.orderedOn == null) || (this.orderedOn.equals(paramDate))))
break label39;
$$_hibernate_trackChange("orderedOn");
label39:
Date localDate = paramDate;
if ($$_hibernate_getInterceptor() != null)
localDate = (Date) $$_hibernate_getInterceptor().writeObject(this, "orderedOn", this.orderedOn, paramDate);
this.orderedOn = localDate;
}
public Object $$_hibernate_getEntityInstance() {
return this;
}
public EntityEntry $$_hibernate_getEntityEntry() {
return this.$$_hibernate_entityEntryHolder;
}
public void $$_hibernate_setEntityEntry(EntityEntry paramEntityEntry) {
this.$$_hibernate_entityEntryHolder = paramEntityEntry;
}
public ManagedEntity $$_hibernate_getPreviousManagedEntity() {
return this.$$_hibernate_previousManagedEntity;
}
public void $$_hibernate_setPreviousManagedEntity(ManagedEntity paramManagedEntity) {
this.$$_hibernate_previousManagedEntity = paramManagedEntity;
}
public ManagedEntity $$_hibernate_getNextManagedEntity() {
return this.$$_hibernate_nextManagedEntity;
}
public void $$_hibernate_setNextManagedEntity(ManagedEntity paramManagedEntity) {
this.$$_hibernate_nextManagedEntity = paramManagedEntity;
}
}
很容易意识到,新的字节码增强逻辑与以前的InstrumentTask生成的逻辑不同。
与自定义脏检查机制类似,新的字节码增强版本记录了已更改的属性,而不仅仅是一个简单的脏布尔标志。 更改时,增强逻辑会标记脏字段。 这种方法比必须将所有当前属性值与加载时快照数据进行比较要有效得多。
我们到了吗?
即使实体类字节码得到了增强,使用Hibernate 4.3.6 仍然以某种方式缺少一些拼图 。
例如,当调用setNumber(Long number)时 ,将执行以下拦截方法:
private void $$_hibernate_write_number(Long paramLong) {
if (($$_hibernate_getInterceptor() == null) || ((this.number == null) || (this.number.equals(paramLong))))
break label39;
$$_hibernate_trackChange("number");
label39:
Long localLong = paramLong;
if ($$_hibernate_getInterceptor() != null)
localLong = (Long) $$_hibernate_getInterceptor().writeObject(this, "number", this.number, paramLong);
this.number = localLong;
}
在我的示例中,$$ _ hibernate_getInterceptor()始终为null,从而绕过$$ _ hibernate_trackChange(“ number”)调用。 因此,将不会记录任何脏属性,从而迫使Hibernate退回到默认的深度比较脏检查算法 。
因此,即使Hibernate在该特定领域取得了长足的进步,肮脏检查的增强仍然需要额外的工作才能变得容易获得。
- 代码可在GitHub上获得 。
翻译自: https://www.javacodegeeks.com/2014/09/hibernate-bytecode-enhancement.html