我正在研究使用JPA进行对象持久化的java的独立应用程序,而提供程序是Hibernate。现在,当我持久化实体并调用EntityTransaction的commit()时,实体将持久保存到数据库。如果约束等存在数据库错误,则存在异常,当我尝试回滚时,我得到一个java.lang.IllegalStateException。什么都没有得到承诺。
我有一个Role实体,它使用表生成策略为该实体生成主键。每当提交失败并且下次保存角色时,为先前保存生成的序列将丢失,并且这次是递增的。此问题也会持续存在于自动增量策略中。使用hibernate SessionFactory时,我没有遇到这个问题。
代码:
Role.java
@Entity
@Table(schema="System")
public class Role extends PrincipalEntityBase{
@TableGenerator(schema="System",table="MasterSequence", valueColumnName="Sequence",
pkColumnName="GenKey",pkColumnValue="Role_ID",
name="System.Role", allocationSize=0)
@Id
@GeneratedValue(generator="System.Role",strategy=GenerationType.TABLE)
@Column(name="Role_ID")
private Long role_ID;
@Column(name="RoleName")
private String roleName;
public Role() {
}
public Long getRole_ID() {
return this.role_ID;
}
public void setRole_ID(Long role_ID) {
this.role_ID = role_ID;
}
public String getRoleName() {
return this.roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Main.java
public static void main(String[] args){
Role r = new Role();
r.setRoleName("Hello");
EntityManager em = persistence.createEntityManagerFactory("test")
.createEntityManager();
EntityTransaction tr = em.getTransaction();
try {
tr.begin();
em.persist(r);
tr.commit();
} catch (Exception e) {
e.printStackTrace();
tr.rollback();
System.out.println("Rolled back");
}
}
StackTrace:
javax.persistence.RollbackException: Error while committing the transaction
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:93)
at com.mis.jpa.test.main.Main.main(Main.java:32)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [com.mis.entity.system.Role]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1235)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:81)
... 1 more
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [com.mis.entity.system.Role]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
Exception in thread "main" java.lang.IllegalStateException: Transaction not active
at org.hibernate.ejb.TransactionImpl.rollback(TransactionImpl.java:104)
at com.mis.jpa.test.main.Main.main(Main.java:35)
Db服务器是MSSQL服务器。
Persistence.xml
<persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.mis.entity.system.Role</class>
<properties>
<!-- Connection properties -->
<property name="javax.persistence.jdbc.driver"
value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="javax.persistence.jdbc.url" value="jdbc:sqlserver://192.168.1.2:1433;databaseName=jparesearch" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="admin" />
<!-- JPA Provider Settings -->
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
答案 0 :(得分:4)
第一个问题:为什么回滚失败。回滚失败,因为它不是在抛出异常的提交之前的某些代码,而是提交本身。如果commit抛出RollbackException,则表示提交失败,因此将提交转换为回滚并使事务处于非活动状态。只需在回滚事务之前测试事务是否处于活动状态。
第二个问题:为什么生成的ID序列中存在漏洞。当被要求提供新ID时,生成器会在其自身的短期事务中递增序列号。因此,如果您的事务回滚并因此不使用生成的ID,则序列中将存在漏洞,因为表中的增量已经提交。如果它没有使用它自己的短期事务,那么,要么你会有很多冲突(因为几个长事务会看到相同的当前值并且稍后才增加它),或者吞吐量会非常低,因为所有想要ID的交易必须等待先前的交易完成。
洞是不可避免的。处理它们。