我正在尝试从表中获取记录。所以我使用了以下本机查询(自联接)来获取。我在Windows上使用Eclipse IDE,EclipseLink JPA2和MySQL。
select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " + <offset> + "," + <iTotalRecords> + ") as topconv on detail.timestamp=topconv.maxtimestamp order by detail.timestamp
我遇到了多线程带来的死锁问题。我不确定它是JPA死锁还是MySQL死锁。
我有以下pojo类
@Entity
@Table(name="mytable")
public class MyTable {
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Column(name="mytableid")
private long lMyTableId;
@ManyToOne
@JoinColumn(name="id", referencedColumnName="id")
private User user; //this object has id, name and so on fields.
@Column(name="timestamp", length=15, nullable=false)
private String strTimestamp;
@Column(name="hnumber", nullable=true)
private String hNumber;
//Getters and Setters.
}
以下是获取记录的方法
public List<MyTable> fetchRecords(User theUser, int iTotalRecords, long offset) throws Exception {
if(null == theUser) {
throw new Exception("Invalid input.");
}
EntityManager entityManager = getEntityManager();
if(false == entityManager.getTransaction().isActive()) {
entityManager.getTransaction().begin();
}
try {
Query query = entityManager.createNativeQuery("select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " + offset + "," + iTotalRecords + ") as topconv on detail.timestamp=topconv.maxtimestamp order by detail.timestamp", MyTable.class);
@SuppressWarnings("unchecked")
List<MyTable> resultList = query.getResultList();
return resultList;
} catch(Throwable th) {
throw new Exception(th.getMessage());
} finally {
closeEntityManager();
}
}
获取并关闭EntityManager
private EntityManagerFactory _entityManagerFactory = Persistence.createEntityManagerFactory("JPATest");
protected EntityManager getEntityManager() {
_entityManager = _entityManagerFactory.createEntityManager();
return _entityManager;
}
protected void closeEntityManager() {
try {
if(null != _entityManager) {
_entityManager.close();
}
} catch(Throwable th){}
}
我的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="JPATest">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.company.service.db.pojo.User</class>
<class>com.company.service.db.pojo.MyTable</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/mydb"></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-or-extend-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="1000"/>
<property name="eclipselink.target-database" value="MySQL"/>
</properties>
</persistence-unit>
</persistence>
我的应用程序支持多线程。
为了复制死锁,我运行了100个线程,每个线程执行fetchRecords方法。每次获取的记录几乎大约200或更多。
请让我知道如何解决这个问题。任何帮助表示赞赏。
答案 0 :(得分:0)
根据您的描述,所有线程仅执行此只读查询。由于“锁定”而非“死锁”,您可能体验的内容可能更长。两者都是不同的东西,锁定本身不是问题,除非它当然是持续很长时间。锁定是数据库固有的,可提供一致的数据视图,并且存在不同类型/级别的锁。您可以阅读更多相关信息here。粗略看一下你的SQL是常用的选择,所以我也没有看到任何锁定的原因。要获取有关锁定的信息,请参阅this 一旦你确定它的查询需要花费时间,你可以使用EXPLAIN来微调你的查询。 Here是一篇很棒的文章。也许合适的索引对你也有帮助。
但是为了清除您的怀疑,您已经拥有优秀的工具,您可以在MySQL命令提示符下运行以下
SHOW ENGINE INNODB STATUS\g
这将为您提供有关死锁的信息(如果有的话)。
答案 1 :(得分:0)
以下是遇到此问题的人的解决方案。连接不会被JPA关闭它是否已激活。这意味着以下我必须评论代码以开始交易。
public List<MyTable> fetchRecords(User theUser, int iTotalRecords, long offset) throws Exception {
if(null == theUser) {
throw new Exception("Invalid input.");
}
EntityManager entityManager = getEntityManager();
//if(false == entityManager.getTransaction().isActive()) {
// entityManager.getTransaction().begin();
//}
try {
Query query = entityManager.createNativeQuery("select * from mytable as detail join (select max(timestamp) as maxtimestamp from mytable where id=" + theUser.getId() + " group by hnumber order by maxtimestamp limit " + offset + "," + iTotalRecords + ") as topconv on detail.timestamp=topconv.maxtimestamp order by detail.timestamp", MyTable.class);
@SuppressWarnings("unchecked")
List<MyTable> resultList = query.getResultList();
return resultList;
} catch(Throwable th) {
throw new Exception(th.getMessage());
} finally {
closeEntityManager();
}
}
答案 2 :(得分:0)
根据上面给出的信息,我可以猜出以下内容:
1.-代码有begin()
但缺少commit()
或等同于释放事务,正如您指出@ User12111111。但是,由于您选择在解决方案中删除它,请确保您位于包含托管的服务器中。如果你不是,还有更多:
2.-如果在后续周期内重用持久性单元MyTable
,则仍可以管理生成的查询JPATest
对象。如果您不需要更新生成的对象,则make sure to detach them。
3.-此外,当查询锁定太多行时,锁会升级到漏洞表(出于性能原因)。这可能会在您认为不应该创建死锁。如果您不需要行锁定make the query it read-only,正如@Shailendra所指出的那样。
4.-最后但并非最不重要的一点是,如果你不需要重复读取等等,你可以通过adjusting the isolation level对死锁进行限制较少。