我正在尝试添加/获取/更新代码,并且我使用了 EclipseLink 提供程序和 JPA2.0。和MySQL。
下面的代码抛出一个错误,指出发生了死锁。问题是随机发生的。我想知道如何处理死锁。
这是错误信息:
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: null, message from server: "Deadlock found when trying to get lock; try restarting transaction"
Error Code: 1213
Call: UPDATE activitylog SET timestampdate = ? WHERE (logid = ?)
bind => [2013-11-19 20:10:38.583, 1]
Query: UpdateObjectQuery(test.ActivityLog@dd3314)
这是我正在尝试的代码:
public class TestMain {
public static void main(String[] args) {
for(int j = 0; j < 10; j ++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
ActivityLogDAO activityLogDAO = new ActivityLogDAO();
try {
ActivityLog theActivityLog = new ActivityLog();
theActivityLog.setTimestampdate(new Date());
theActivityLog.setMyId(i);
activityLogDAO.insert(theActivityLog);
ActivityLog activityLog = activityLogDAO.getActivityLog(theActivityLog);
activityLog.setTimestampdate(new Date());
activityLogDAO.update(activityLog);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
}
}
这是实体类
@Entity
@Table(name="activitylog")
public class ActivityLog implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Column(name="logid")
private long logid;
@Column(name="myid")
private long lMyId;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="timestampdate", nullable=true)
private Date timestampdate;
public long getMyId() {
return lMyId;
}
public void setMyId(long lMyId) {
this.lMyId = lMyId;
}
public long getLogid() {
return logid;
}
public void setLogid(long logid) {
this.logid = logid;
}
public Date getTimestampdate() {
return timestampdate;
}
public void setTimestampdate(Date timestampdate) {
this.timestampdate = timestampdate;
}
}
这是我的 DAO 类:
public class ActivityLogDAO {
private EntityManagerFactory _entityManagerFactory = null;
private EntityManager _entityManager = null;
public ActivityLogDAO() {
_entityManagerFactory = Persistence.createEntityManagerFactory("MyTestOnLock");
_entityManager = _entityManagerFactory.createEntityManager();
}
protected EntityManager getEntityManager() {
return _entityManager;
}
protected void setEntityManager(EntityManager _entityManager) {
this._entityManager = _entityManager;
}
public ActivityLog insert(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("inserting");
getEntityManager().persist(theActivityLog);
getEntityManager().getTransaction().commit();
System.out.println("inserted");
return theActivityLog;
}
public ActivityLog getActivityLog(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to get object");
Query query = getEntityManager().createQuery("SELECT m FROM ActivityLog m WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
//deadlock happens here.
@SuppressWarnings("unchecked")
List<ActivityLog> resultList = query.getResultList();
System.out.println(resultList.size());
System.out.println("got object");
if(null == resultList || 0 == resultList.size()) {
return null;
} else {
return resultList.get(0);
}
}
public ActivityLog update(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to update object");
Query query = getEntityManager().createQuery("UPDATE ActivityLog m SET m.timestampdate = :timestampdate WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
query.setParameter("timestampdate", theActivityLog.getTimestampdate());
int executeUpdate = query.executeUpdate();
getEntityManager().getTransaction().commit();
System.out.println("object updted.");
if(0 == executeUpdate) {
return null;
}
return theActivityLog;
}
}
这是我的 persistance.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="MyTestOnLock">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>test.ActivityLog</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"></property>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/locktest"></property>
<property name="javax.persistence.jdbc.user" value="root"></property>
<property name="javax.persistence.jdbc.password" value="root"></property>
<!-- EclipseLink should create the database schema automatically -->
<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
<property name="eclipselink.id-validation" value="NULL"></property>
<property name="eclipselink.logging.level" value="FINE"/>
<property name="javax.persistence.lock.timeout" value="100"/>
<property name="eclipselink.order-updates" value="true"/>
<property name="eclipselink.connection-pool.sequence" value="max" />
<property name="eclipselink.ddl-generation.output-mode" value="database" />
<property name="eclipselink.target-database" value="MySQL" />
</properties>
</persistence-unit>
</persistence>
当 AcitivityDAO 尝试更新时,会发生死锁。为什么要处理或避免死锁问题?
感谢任何帮助!!
我收到以下错误:
javax.persistence.PersistenceException: java.lang.NullPointerException
和
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Deadlock found when trying to get lock; Try restarting transaction, message from server: "Lock wait timeout exceeded; try restarting transaction"
Error Code: 1205
Call: UPDATE activitylog SET timestampdate = ? WHERE (myid = ?)
bind => [2013-11-20 16:54:09.646, 0]
Query: UpdateAllQuery(referenceClass=ActivityLog sql="UPDATE activitylog SET timestampdate = ? WHERE (myid = ?)")
我使用了@Chris Ridal 指定的相同代码。
这是代码:基本上我尝试多次运行 MainTest 类。
public class MainTest {
public static void main(String[] args) {
updateActivityLog();
}
private static void updateActivityLog() {
final PersistenceController persistenceController = new PersistenceController(Persistence.createEntityManagerFactory("MyTestOnLock"));
for (int i = 0; i < 100; i++) {
try {
for(int j = 0; j < 200; j++) {
ActivityLog theActivityLog = new ActivityLog();
theActivityLog.setMyId(j);
theActivityLog.setTimestampdate(new Date());
persistenceController.update(theActivityLog);
}
} catch (Exception e) {
e.printStackTrace();
}
}
persistenceController.commitAndClose();
}
}
public class PersistenceController {
private EntityManager manager;
public PersistenceController(EntityManagerFactory factory)
{
/*
* Normally you want to split your work up into separate transactions
* (ie new entity managers), in a logical way which will depend on how
* your application works. This class will do that for you if you keep
* your factory. Note that factory's are expensive to create but entity
* managers are cheap to create.
*/
manager = factory.createEntityManager();
manager.getTransaction().begin();
}
// Call ONCE on an object after creating it, it will stay in sync with the database even when you change it remotely
public void persist(Serializable entityObj)
{
manager.persist(entityObj);
manager.flush();
}
// Call to sync with database (even though you might not actually see the objects in the database until you commit)
public void flush()
{
manager.flush();
}
/*
* Call when you are done with your unit of work to commit the DB changes
*/
public void commitAndClose()
{
manager.getTransaction().commit();
manager.close();
}
public ActivityLog getActivityLog(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == manager.getTransaction().isActive()) {
manager.getTransaction().begin();
}
System.out.println("trying to get object");
Query query = manager.createQuery("SELECT m FROM ActivityLog m WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
@SuppressWarnings("unchecked")
List<ActivityLog> resultList = query.getResultList();
System.out.println(resultList.size());
System.out.println("got object");
if(null == resultList || 0 == resultList.size()) {
return null;
} else {
return resultList.get(0);
}
}
public ActivityLog update(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
if(false == manager.getTransaction().isActive()) {
manager.getTransaction().begin();
}
ActivityLog activityLog = getActivityLog(theActivityLog);
activityLog.setTimestampdate(theActivityLog.getTimestampdate());
persist(activityLog);
return theActivityLog;
}
}
是否必须为每个数据库插入、合并、更新或删除获取 EntityManager?看下面的代码,我没有看到死锁发生。请确认。
public class ActivityLogDAO {
private EntityManagerFactory _entityManagerFactory = null;
private EntityManager _entityManager = null;
public ActivityLogDAO() {
_entityManagerFactory = Persistence.createEntityManagerFactory("MyTestOnLock");
}
protected EntityManager getEntityManager() {
return _entityManager;
}
protected void setEntityManager(EntityManager _entityManager) {
this._entityManager = _entityManager;
}
public ActivityLog insert(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
_entityManager = _entityManagerFactory.createEntityManager();
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("inserting");
getEntityManager().persist(theActivityLog);
getEntityManager().getTransaction().commit();
System.out.println("inserted");
return theActivityLog;
}
public ActivityLog getActivityLog(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
_entityManager = _entityManagerFactory.createEntityManager();
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to get object");
Query query = getEntityManager().createQuery("SELECT m FROM ActivityLog m WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
//deadlock happens here.
@SuppressWarnings("unchecked")
List<ActivityLog> resultList = query.getResultList();
System.out.println(resultList.size());
System.out.println("got object");
if(null == resultList || 0 == resultList.size()) {
return null;
} else {
return resultList.get(0);
}
}
public ActivityLog update(ActivityLog theActivityLog) throws Exception {
if(null == theActivityLog) {
throw new Exception("Invalid ActivityLog Object");
}
_entityManager = _entityManagerFactory.createEntityManager();
if(false == getEntityManager().getTransaction().isActive()) {
getEntityManager().getTransaction().begin();
}
System.out.println("trying to update object");
Query query = getEntityManager().createQuery("UPDATE ActivityLog m SET m.timestampdate = :timestampdate WHERE m.lMyId = :lMyId");
query.setParameter("lMyId", theActivityLog.getMyId());
query.setParameter("timestampdate", theActivityLog.getTimestampdate());
int executeUpdate = query.executeUpdate();
getEntityManager().getTransaction().commit();
System.out.println("object updted.");
if(0 == executeUpdate) {
return null;
}
return theActivityLog;
}
}
最佳答案
一般you do not need to use DAO's when using JPA .
相反,您可能希望使用这样的类(未经测试),带来您自己的 EntityManagerFactory:
public class PersistenceController
{
private EntityManager manager;
public PersistenceController(EntityManagerFactory factory)
{
/*
* Normally you want to split your work up into separate transactions
* (ie new entity managers), in a logical way which will depend on how
* your application works. This class will do that for you if you keep
* your factory. Note that factory's are expensive to create but entity
* managers are cheap to create.
*/
manager = factory.createEntityManager();
manager.getTransaction().begin();
}
// Call ONCE on an object after creating it, it will stay in sync with the database even when you change it remotely
public void persist(Serializable entityObj)
{
manager.persist(entityObj);
}
// Call to sync with database (even though you might not actually see the objects in the database until you commit)
public void flush()
{
manager.flush();
}
/*
* Call when you are done with your unit of work to commit the DB changes
*/
public void commitAndClose()
{
manager.getTransaction().commit();
manager.close();
}
}
要使用它,您可以在创建对象时调用 persist(entityObj)
,调用 flush()
与数据库同步(如果需要)和commitAndClose()
完成后。将 PersistenceController
放在您需要保留对象或使用其其他操作时可以发布到它的地方。
现在您的事务不会并发发生,您也不会遇到死锁。
注意:在生产代码中,您将使用更多的异常管理并将您的工作拆分为不同的 EntityManager 事务,如果您在逻辑上配置和创建此 PersistenceController
类,则此类会为您执行此操作。
关于java - JPA 2.0 如何处理死锁(Eclipselink JPA2.0 MySQL),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20074583/