英文:
Why is my transaction rolled back unexpectedly?
问题
在我们的项目中,我们正在尝试让当前部署在WebSphere上的代码能够在Liberty上正常运行。
在接收到消息后,消息驱动Bean(MDB)会调用服务中的一个方法,将数据插入到DB2数据库中。
问题在于,在调用flush时,日志中会出现以下错误:
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1136) ~[hibernate-entitymanager-5.1.3.Final.jar:5.1.3.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1297) ~[hibernate-entitymanager-5.1.3.Final.jar:5.1.3.Final]
[ERROR ] WLTC0017E: Resources rolled back due to setRollbackOnly() being called.
没有其他错误,比如SQL查询不正确之类的。
由于这段代码在WebSphere中运行正常,我想知道是否在配置中出现了问题,或者在处理事务方面,Liberty是否有所不同。
以下是相关的代码和配置片段:
@Service
public class IdentificationService implements IIdentificationService {
@Autowired
private IIdentificationDAO identificationDAO;
@Transactional
public Identification insertIdentificationData(RequestForIdentificationRequest request, String referenceNumber) {
final Identification identification = new Identification();
...
identificationDAO.create(identification);
LOGGER.debug("Identification record saved with id: {}", identification.getId());
return identification;
}
}
@Repository
public class IdentificationDAO extends BaseDAO implements IIdentificationDAO {
@Override
public void create(final Identification identification) {
insertRecord(identification);
}
}
public abstract class BaseDAO {
@PersistenceContext(name = "UserData", unitName = "UserData")
private EntityManager em;
public <T> void insertRecord(final T record) {
em.persist(record);
em.flush();
}
}
public class Identification implements Serializable {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
@SequenceGenerator(sequenceName = "IDENTIFICATION_SEQ", name = "sequenceGenerator", allocationSize = 1)
private Long id;
...
}
applicationContext.xml
<jee:jndi-lookup id="userDataSource" jndi-name="jdbc/userdataDB" />
<bean id="userDataEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="UserData" />
<property name="persistenceXmlLocation" value="classpath:persistence-userdata.xml" />
<property name="dataSource" ref="userDataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.DB2Dialect" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="userDataEntityManagerFactory" />
</bean>
server.xml
<library id="DB2JCCLib">
<fileset dir="/etc/liberty/jdbc" includes="db2jcc4.jar, db2jcc_license_cisuz.jar, db2jcc_license_cu.jar"/>
</library>
<dataSource id="UserDB" jndiName="jdbc/userdataDB" type="javax.sql.XADataSource"
isolationLevel="TRANSACTION_READ_COMMITTED">
<jdbcDriver libraryRef="DB2JCCLib" javax.sql.XADataSource="com.ibm.db2.jcc.DB2XADataSource"/>
<properties.db2.jcc serverName="${datasource.userdb.serverName}" portNumber="${datasource.userdb.portNumber}"
databaseName="${datasource.userdb.databaseName}"
user="user" password="password"/>
</dataSource>
更新:问题原来是配置中存在多个具有相同名称的事务管理器bean,它们指向不同的数据库。
为它们分配唯一的名称,并在Transactional注解中显式引用它们解决了问题。
英文:
In our project, we are trying to get code that is currently deployed on WebSphere working on Liberty instead.
After receiving a message an MDB calls a method in a service which inserts the data into a DB2 database.
The problem is that when flush is called, we get the following errors in the log:
javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1136) ~[hibernate-entitymanager-5.1.3.Final.jar:5.1.3.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1297) ~[hibernate-entitymanager-5.1.3.Final.jar:5.1.3.Final]
[ERROR ] WLTC0017E: Resources rolled back due to setRollbackOnly() being called.
There are no other errors, like SQL query not being correct or something like that.
Since this code is working fine in WebSphere, I'm wondering if there is something wrong in the configuration or if Liberty does something different in handling transactions.
Below are the relevant pieces of code and configuration:
@Service
public class IdentificationService implements IIdentificationService {
@Autowired
private IIdentificationDAO identificationDAO;
@Transactional
public Identification insertIdentificationData(RequestForIdentificationRequest request, String referenceNumber) {
final Identification identification = new Identification();
...
identificationDAO.create(identification);
LOGGER.debug("Identification record saved with id: {}", identification.getId());
return identification;
}
}
@Repository
public class IdentificationDAO extends BaseDAO implements IIdentificationDAO {
@Override
public void create(final Identification identification) {
insertRecord(identification);
}
}
public abstract class BaseDAO
{
@PersistenceContext(name = "UserData", unitName = "UserData")
private EntityManager em;
/**
* @param record
*/
public <T> void insertRecord(final T record)
{
em.persist(record);
em.flush();
}
}
public class Identification implements Serializable {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
@SequenceGenerator(sequenceName = "IDENTIFICATION_SEQ", name = "sequenceGenerator", allocationSize = 1)
private Long id;
...
}
applicationContext.xml
<jee:jndi-lookup id="userDataSource" jndi-name="jdbc/userdataDB" />
<bean id="userDataEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="UserData" />
<property name="persistenceXmlLocation" value="classpath:persistence-userdata.xml" />
<property name="dataSource" ref="userDataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
<property name="databasePlatform" value="org.hibernate.dialect.DB2Dialect" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="userDataEntityManagerFactory" />
</bean>
server.xml
<library id="DB2JCCLib">
<fileset dir="/etc/liberty/jdbc" includes="db2jcc4.jar, db2jcc_license_cisuz.jar, db2jcc_license_cu.jar"/>
</library>
<dataSource id="UserDB" jndiName="jdbc/userdataDB" type="javax.sql.XADataSource"
isolationLevel="TRANSACTION_READ_COMMITTED">
<jdbcDriver libraryRef="DB2JCCLib" javax.sql.XADataSource="com.ibm.db2.jcc.DB2XADataSource"/>
<properties.db2.jcc serverName="${datasource.userdb.serverName}" portNumber="${datasource.userdb.portNumber}"
databaseName="${datasource.userdb.databaseName}"
user="user" password="password"/>
</dataSource>
UPDATE: The problem turned out to be multiple transaction manager beans (named the same) in the configuration pointing at different databases.
Giving them unique names and referring to them explicitly in the Transactional annotation did the trick
专注分享java语言的经验与见解,让所有开发者获益!
评论