交易在服务和道服务层
我有一个关于@Transactional注释的问题。 没有什么特别的定义,所以据我所知PROPAGATION_REQUIRED 假设我有一个在服务和dao层上的事务注释。交易在服务和道服务层
服务
@Transactional
public long createStudentInDB(Student student) {
final long id = addStudentToDB (student);
addStudentToCourses (id, student.getCourseIds());
return id;
}
private long addStudentToDB (Student student) {
StudentEntity entity = new StudentEntity();
convertToEntity(student, entity);
try {
final id = dao.create(entity);
} catch (Exception e){
//
}
return id;
}
private void addStudentToCourses (long studentId, List<String> coursesIds){
//add user to group
if(coursesIds!= null){
List<StudentCourseEntity> studentCourses = new ArrayList<>();
for(String coursesId: coursesIds){
StudentCourseEntity entity = new StudentCourseEntity();
entity.setCourseId(coursesId);
entity.setStudentId(userId);
studentCourses.add(studentId);
}
anotherDao.saveAll(studentCourses);
}
}
DAO
@Transactional
public UUID create(StudentEntity entity) {
if (entity == null) { throw new Exception(//…); }
getCurrentSession().save(entity);
return entity.getId();
}
ANOTHERDAO
@Transactional
public void saveAll(Collection<StudentCourseEntity> studentCourses) {
List<StudentCourseEntity> result = new ArrayList<>();
if(studentCourses!= null) {
for (StudentCourseEntity studentCourse : studentCourses) {
if (studentCourse!= null) {
save(studentCourse);
}
}
}
}
尽管这不是最优的事实,现在看来,这导致死锁。 假设我有最多2个连接到数据库。 而我正在使用3个不同的线程来运行相同的代码。 线程1和线程2接收连接,线程3没有获得任何连接。 除此之外,看起来thread-1在尝试获取dao级别的连接时变得卡住,与thread-2相同。造成僵局。
我确信通过使用propagation_required这不会发生。 我错过了什么吗? 这是什么建议?有没有一种方法可以在两个层上都有@transactional?如果不是哪个是首选? 感谢 法布里奇奥
由于dao.doSomeStuff预计将调用来自其他交易中,我建议您配置此方法为:
@Transaction(propagation=REQUIRES_NEW)
多亏了这是调用此方法将交易停止,直到REQUIRES_NEW的那一个完成。
不确定这是否是针对您的特定死锁案例的修复,但您的示例适用于此特定设置。
感谢您的回答。我确实需要完全相反的。我不想被制止。我只是想知道如果我在服务级别接收到连接,现在我正在'重用'该连接。我为什么卡在那? – fabriziomieli
尝试添加方法的实现。 LEts看看发生了什么 –
嗨,我编辑了我的问题。这可能与我使用2种不同的daos的事实有关? – fabriziomieli
你说得对,Propagation.REQUIRED
是默认值。但是这也意味着dao上的第二个(嵌套)调用会加入/重用在服务级别创建的事务。所以不需要为嵌套调用创建另一个事务。
的首选方法是使用Spring的最高水平的基于模板的 持久化API或使用方法:
一般春季(高级别使用)应该由它转发给底层的ORM层管理资源处理本地ORM API与 事务感知工厂bean或用于管理本地 资源工厂的代理。这些事务感知解决方案在内部处理资源创建和重用,清理,可选事务 资源同步和异常映射。因此,用户数据访问代码不必解决这些任务,但可以将 纯粹集中在非模板化持久性逻辑上。
即使你处理一下你自己(在低级别的API使用)的连接应该被重复使用:
当你需要的应用程序代码直接与资源处理 类型的本土持久性API,您可以使用这些类来确保 获得正确的Spring Framework托管实例, 事务(可选)同步,并且在此过程中发生的 的异常正确映射到一致的API。
...
如果现有的交易已经同步 的连接(链接)到它,则返回该实例。否则,方法调用 将触发创建一个新连接,该连接是(可选)同步到任何现有事务的(可选) ,并且可用于在同一事务中随后重用。
也许你必须找到发生了什么事情在那里。
每个会话/工作单元将绑定到一个线程,并在事务结束后释放(连同分配的连接)。当然,当你的线程卡住它不会释放连接。
你确定这个“死锁”是由这个嵌套造成的吗?也许这是另一个原因。你有这个例子的测试代码吗?或者一个线程转储或其他东西?
谢谢你的回答。我明白了。但是,如果情况是这样,并且嵌套调用重用了在服务级别创建的事务,那么它是如何影响获得连接的呢?我读到他们正在使用相同的“物理”连接,但有一个不同的“逻辑”连接......但也许我错过了一些东西! – fabriziomieli
编辑我的答案。也许你可以在这里发布你的测试代码。 –
嗨,我编辑了我的问题。这可能与我使用2种不同的daos的事实有关? – fabriziomieli
@Transactional
通过保持ThreadLocal
状态来工作,该状态可由(Spring管理的)代理EntityManager访问。如果您使用的是Propagation.REQUIRED
(默认值),并且您有一个调用两个不同DAO(或同一个DAO上的两个事务方法)的非事务性方法,您将获得两个事务,并且两个调用获取一个池连接。您可能会获得相同的连接两次或两次不同的连接,但您应该只使用一次连接。
如果您从@Transactional方法中调用两个DAO,则只会有一个事务,因为DAO将查找并加入在ThreadLocal状态中找到的现有事务,同样,您只需要一个来自池的连接。
如果遇到死锁,那么有些东西是非常错误的,您可能想要在创建连接和事务时进行调试。通过调用Connection.setAutoCommit(false)
开始一个事务,在Hibernate中这发生在org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor#begin()
。连接由延伸org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor
的类进行管理,因此这些是放置断点的好地方,并将调用堆栈追溯回代码,以查看哪些线路创建了连接。
如果您的连接不够用,您可能没有正确设置事务,或者自己搞乱连接,而不是让Spring管理事务。只有在没有实现的情况下添加方法签名才能解决您的问题。首先检查你的设置(确保你有正确的tx设置)并检查你的实现。 –