Java 当@Version的类型为整型时休眠重复记录,但在使用基元类型int时不休眠重复记录
给定一个JPA实体,我注意到代码中有一个有趣的问题:Java 当@Version的类型为整型时休眠重复记录,但在使用基元类型int时不休眠重复记录,java,hibernate,Java,Hibernate,给定一个JPA实体,我注意到代码中有一个有趣的问题: 当@Version字段的类型为Integer时,记录将重复 当@Version字段为基元类型int时,记录被正确存储 我使用Hibernate 4.3.11.Final上的Spring数据和内存中的HSQLDB 2.5.0数据库(但使用Hibernate 5.4.10.Final和Oracle 12数据库)在具有双向关系的父/子层次结构上重现了该问题(为了减少冗长,我使用了Lombok) 儿童班: @Entity @SequenceGen
- 当
字段的类型为@Version
时,记录将重复Integer
- 当
字段为基元类型@Version
时,记录被正确存储int
@Entity
@SequenceGenerator(name = "s1", allocationSize = 1, sequenceName = "S1db")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString(exclude = "parent")
class Child {
@Id
@Column
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "s1")
private Long id;
@Column
private String name;
@Version
@Column
private Integer version;
@ManyToOne
@JoinColumn
private Parent parent;
}
父类:
@Entity
@Getter
@Setter
@NoArgsConstructor
@SequenceGenerator(name = "s2", allocationSize = 1, sequenceName = "s2db")
@ToString
class Parent {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "s2")
@Column
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(targetEntity = Child.class,
mappedBy = "parent",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Child> children = new ArrayList<>();
}
@实体
@吸气剂
@塞特
@诺尔格构装师
@SequenceGenerator(name=“s2”,allocationSize=1,sequenceName=“s2db”)
@托斯特林
班级家长{
@身份证
@GeneratedValue(策略=GenerationType.SEQUENCE,generator=“s2”)
@纵队
私人长id;
@列(nullable=false)
私有字符串名称;
@OneToMany(targetEntity=Child.class,
mappedBy=“parent”,
cascade=CascadeType.ALL,
删除=真,
fetch=FetchType.LAZY
)
@OnDelete(action=OnDeleteAction.CASCADE)
private List children=new ArrayList();
}
存储库和Spring配置:
@Repository
@javax.transaction.Transactional
interface ChildRepository extends JpaRepository<Child, Long> {}
@javax.transaction.Transactional
@Repository
interface ParentRepository extends JpaRepository<Parent, Long> {}
@Configuration
@EnableJpaRepositories(basePackageClasses = ParentRepository.class)
@EntityScan(basePackageClasses = Parent.class)
class ConfigClass {
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:mem:spring;sql.syntax_ora=true");
dataSource.setUsername("sa");
dataSource.setPassword("");
final Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.format_sql", "true");
properties.setProperty("hibernate.use_sql_comments", "true");
properties.setProperty("properties.hibernate.jdbc.batch_size", "10");
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(Parent.class.getPackage().getName());
final JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(properties);
return em;
}
}
@存储库
@javax.transaction.Transactional
接口子存储库扩展了JpaRepository{}
@javax.transaction.Transactional
@存储库
接口ParentRepository扩展了JpaRepository{}
@配置
@EnableJpaRepositories(basePackageClasses=ParentRepository.class)
@EntityScan(basePackageClasses=Parent.class)
类配置类{
@豆子
公共平台transactionManager transactionManager(EntityManager工厂EntityManager工厂){
JpaTransactionManager txManager=新的JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
返回txManager;
}
@豆子
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
final DriverManager数据源dataSource=新DriverManager数据源();
setDriverClassName(“org.hsqldb.jdbcDriver”);
setUrl(“jdbc:hsqldb:mem:spring;sql.syntax_ora=true”);
dataSource.setUsername(“sa”);
dataSource.setPassword(“”);
最终属性=新属性();
properties.setProperty(“hibernate.dialent”、“org.hibernate.dialent.hsqldialent”);
properties.setProperty(“hibernate.hbm2ddl.auto”、“create”);
setProperty(“hibernate.show_sql”,“true”);
setProperty(“hibernate.format_sql”,“true”);
setProperty(“hibernate.use_sql_comments”,“true”);
setProperty(“properties.hibernate.jdbc.batch_size”,“10”);
最终LocalContainerEntityManagerFactoryBean em=新的LocalContainerEntityManagerFactoryBean();
em.setDataSource(数据源);
em.setPackagesToScan(Parent.class.getPackage().getName());
final JpaVendorAdapter vendorAdapter=new HibernateJpaVendorAdapter();
em.setjpavendor适配器(供应商适配器);
em.setJpaProperties(属性);
返回em;
}
}
最后是测试课:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ConfigClass.class)
@Transactional
public class ParentRepositoryTest {
@Autowired
private ParentRepository parentRepository;
@Autowired
private ChildRepository childRepository;
@Test
public void test() {
// cleanup database
childRepository.deleteAll();
parentRepository.deleteAll();
// Given a parent without child
Parent p = new Parent();
p.setName("Alice");
p = parentRepository.saveAndFlush(p);
// When I add 3 children
for (int i=0; i<3; i++) {
Child c = new Child();
c.setParent(p);
c.setName("child " + i);
c.setVersion(0); // <===== this is the main issue
// When Child.version is an int => will persist 3 elements
// When Child.version is an Integer => will persist 6 elements
p.getChildren().add(c);
}
for (Child c : p.getChildren()) {
childRepository.save(c);
}
// Then the database contains 3 children
Assertions.assertThat(childRepository.findAll()).hasSize(3); // Ouch! will persist 6 children
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(类=ConfigClass.class)
@交易的
公共类ParentRepositoryTest{
@自动连线
私有父存储库父存储库;
@自动连线
私人儿童知识库;
@试验
公开无效测试(){
//清理数据库
deleteAll();
parentRepository.deleteAll();
//给一个没有孩子的父母
父级p=新父级();
p、 设置名称(“Alice”);
p=parentRepository.saveAndFlush(p);
//当我加上3个孩子
对于(int i=0;我将保留6个元素)
p、 getChildren().add(c);
}
for(Child c:p.getChildren()){
保存(c);
}
//然后数据库包含3个子项
Assertions.assertThat(childRepository.findAll()).hasSize(3);//哎哟!将保留6个孩子
}
}
结论是:
- 当
为原始类型Child.version
时,测试通过int
- 当
为Child.version
类型时,测试失败Integer
int
),但我想知道为什么这会产生不同
有人能解释一下为什么@Version
字段类型会有所不同吗