Cui*_*崔鹏飞 6 java spring hibernate jpa spring-data-jpa
这篇文章中的所有代码都可以在这里找到:https : //github.com/cuipengfei/gs-accessing-data-jpa/tree/master/complete
您可以运行此测试以重现该问题:https : //github.com/cuipengfei/gs-accessing-data-jpa/blob/master/complete/src/test/java/hello/T1ServiceTest.java
我有一个域模型:
@Entity
@Table(name = "t1", schema = "test")
public class T1 extends BaseEntity {
@Column(nullable = false)
private UUID someField;
@Column()
private ZonedDateTime date;
public T1(UUID someField, ZonedDateTime date) {
this.someField = someField;
this.date = date;
}
}
Run Code Online (Sandbox Code Playgroud)
它的字段类型为ZonedDateTime,因此我有一个转换器将其转换为sql时间戳:
@Converter(autoApply = true)
public class ZonedDateTimeAttributeConverter
implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime entityValue) {
return (entityValue == null) ? null :
valueOf(entityValue.withZoneSameInstant(of("UTC")).toLocalDateTime());
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp databaseValue) {
return (databaseValue == null) ? null : databaseValue.toLocalDateTime().atZone(
of("UTC"));
}
}
Run Code Online (Sandbox Code Playgroud)
当我尝试在这样的事务中创建很多T1时:
@Service
public class T1Service {
private static final Logger log = LoggerFactory.getLogger(T1Service.class);
@Autowired
T1Repository t1Repository;
@Transactional
public void insertMany() {
for (int i = 0; i < 1000; i++) {
log.info("!!! " + (i + 1) + "th item start");
UUID randomUUID = UUID.randomUUID();
T1 foundT1 = tryToFindExistingT1(randomUUID);//certainly won't find
if (foundT1 == null) {
log.info("t1 not found");
ZonedDateTime date = now();
//date = null;
//if you enable the line above, there won't be any update statements anymore
//and find will also become faster
T1 t1 = new T1(randomUUID, date);
saveT1(t1);
}
log.info("!!! " + (i + 1) + "th item finished");
log.info("====================================");
}
}
private T1 tryToFindExistingT1(UUID someField) {
long start = currentTimeMillis();
T1 t1Id = t1Repository.findBySomeField(someField);
//as nth item increases, the line above will become very very slow
//and also, there will be more and more update statements
//but if you set date of t1 to null, update statement will disappear and it'll not be slow
log.info("find took: " + (currentTimeMillis() - start) + " milliseconds");
return t1Id;
}
private T1 saveT1(T1 t1) {
long start = currentTimeMillis();
T1 savedT1 = t1Repository.save(t1);
log.info("save took: " + (currentTimeMillis() - start) + " milliseconds");
return savedT1;
}
}
Run Code Online (Sandbox Code Playgroud)
休眠将为整个t1表生成更新语句。
这是for循环前几轮的日志:
2017-03-20 17:51:54.039 INFO 74789 --- [ main] hello.T1Service : !!! 1th item start
2017-03-20 17:51:54.052 INFO 74789 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select t1x0_.id as id1_0_, t1x0_.date as date2_0_, t1x0_.some_field as some_fie3_0_ from test.t1 t1x0_ where t1x0_.some_field=?
2017-03-20 17:51:54.154 INFO 74789 --- [ main] hello.T1Service : find took: 114 milliseconds
2017-03-20 17:51:54.154 INFO 74789 --- [ main] hello.T1Service : t1 not found
2017-03-20 17:51:54.177 INFO 74789 --- [ main] hello.T1Service : save took: 15 milliseconds
2017-03-20 17:51:54.177 INFO 74789 --- [ main] hello.T1Service : !!! 1th item finished
2017-03-20 17:51:54.177 INFO 74789 --- [ main] hello.T1Service : ====================================
2017-03-20 17:51:54.177 INFO 74789 --- [ main] hello.T1Service : !!! 2th item start
Hibernate: insert into test.t1 (date, some_field, id) values (?, ?, ?)
Hibernate: update test.t1 set date=?, some_field=? where id=?
Hibernate: select t1x0_.id as id1_0_, t1x0_.date as date2_0_, t1x0_.some_field as some_fie3_0_ from test.t1 t1x0_ where t1x0_.some_field=?
2017-03-20 17:51:54.194 INFO 74789 --- [ main] hello.T1Service : find took: 17 milliseconds
2017-03-20 17:51:54.194 INFO 74789 --- [ main] hello.T1Service : t1 not found
2017-03-20 17:51:54.195 INFO 74789 --- [ main] hello.T1Service : save took: 1 milliseconds
2017-03-20 17:51:54.195 INFO 74789 --- [ main] hello.T1Service : !!! 2th item finished
2017-03-20 17:51:54.195 INFO 74789 --- [ main] hello.T1Service : ====================================
2017-03-20 17:51:54.195 INFO 74789 --- [ main] hello.T1Service : !!! 3th item start
Hibernate: insert into test.t1 (date, some_field, id) values (?, ?, ?)
Hibernate: update test.t1 set date=?, some_field=? where id=?
Hibernate: update test.t1 set date=?, some_field=? where id=?
Hibernate: select t1x0_.id as id1_0_, t1x0_.date as date2_0_, t1x0_.some_field as some_fie3_0_ from test.t1 t1x0_ where t1x0_.some_field=?
2017-03-20 17:51:54.200 INFO 74789 --- [ main] hello.T1Service : find took: 4 milliseconds
2017-03-20 17:51:54.200 INFO 74789 --- [ main] hello.T1Service : t1 not found
2017-03-20 17:51:54.200 INFO 74789 --- [ main] hello.T1Service : save took: 0 milliseconds
2017-03-20 17:51:54.200 INFO 74789 --- [ main] hello.T1Service : !!! 3th item finished
2017-03-20 17:51:54.200 INFO 74789 --- [ main] hello.T1Service : ====================================
2017-03-20 17:51:54.200 INFO 74789 --- [ main] hello.T1Service : !!! 4th item start
Hibernate: insert into test.t1 (date, some_field, id) values (?, ?, ?)
Hibernate: update test.t1 set date=?, some_field=? where id=?
Hibernate: update test.t1 set date=?, some_field=? where id=?
Hibernate: update test.t1 set date=?, some_field=? where id=?
Hibernate: select t1x0_.id as id1_0_, t1x0_.date as date2_0_, t1x0_.some_field as some_fie3_0_ from test.t1 t1x0_ where t1x0_.some_field=?
2017-03-20 17:51:54.209 INFO 74789 --- [ main] hello.T1Service : find took: 9 milliseconds
2017-03-20 17:51:54.209 INFO 74789 --- [ main] hello.T1Service : t1 not found
2017-03-20 17:51:54.210 INFO 74789 --- [ main] hello.T1Service : save took: 1 milliseconds
2017-03-20 17:51:54.210 INFO 74789 --- [ main] hello.T1Service : !!! 4th item finished
2017-03-20 17:51:54.210 INFO 74789 --- [ main] hello.T1Service : ====================================
Run Code Online (Sandbox Code Playgroud)
正如您在日志中看到的那样,休眠将生成越来越多的更新语句。
看起来更新语句的数量总是与等待提交的T1的行数相同。
现在,如果我卸下转换器,此问题将消失。
我可以使用hibernate-java8 lib代替此转换器来达到相同的效果,但是为什么会发生这种情况?
为什么JPA AttributeConverter使休眠状态在事务中的整个表上生成更新语句?
小智 6
我通过在 getter 和 setter 方法中应用转换器而不是作为注释来解决这个问题。像这样:
@Entity
@Table(name = "t1", schema = "test")
public class T1 extends BaseEntity {
@Column(nullable = false)
private UUID someField;
@Column()
private Timestamp date;
public T1(UUID someField, ZonedDateTime date) {
this.someField = someField;
this.date = date;
}
public void setDate(ZonedDateTime date) {
this.date = new ZonedDateTimeAttributeConverter().convertToDatabaseColumn(date);
}
public ZonedDateTime getDate() {
return new ZonedDateTimeAttributeConverter().convertToEntityAttribute(date);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
865 次 |
| 最近记录: |