我在Spring世界中相当新,我正在尝试实现一个简单的Hibernate DAO,但我对删除和更新操作
有一些疑问我正在使用Spring 3.2.1和Hibernate 4.1.9以及MySQL数据库。
因此,在MySQL数据库中,我有一个名为 person 的表,具有以下结构:
mysql> describe person;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| pid | int(11) | NO | PRI | NULL | auto_increment |
| firstname | varchar(255) | YES | | NULL | |
| lastname | varchar(255) | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
在我的Spring应用程序中,我为DAO定义了以下接口,定义了所需的CRUD操作:
package org.andrea.myexample.HibernateOnSpring.dao;
import java.util.List;
import org.andrea.myexample.HibernateOnSpring.entity.Person;
public interface PersonDAO {
public void addPerson(Person p);
public Person getById(int id);
public List<Person> getPersonsList();
public void delete(int id);
public void update(Person person);
}
然后我以这种方式通过类 PersonDAOImplement 实现这个接口:
package org.andrea.myexample.HibernateOnSpring.dao;
import java.util.List;
import org.andrea.myexample.HibernateOnSpring.entity.Person;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;
public class PersonDAOImpl implements PersonDAO {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
// Metodo che inserisce un nuovo record nella tabella person
@Transactional(readOnly = false)
public void addPerson(Person p) {
Session session = sessionFactory.openSession();
session.save(p);
session.close();
}
/*
* Metodo che recupera un record, rappresentante una persona, avente uno
* specifico id dalla tabella.
*
* @param L'id univoco della persona
*/
public Person getById(int id) {
Session session = sessionFactory.openSession();
try {
return (Person) session.get(Person.class, id);
} finally {
session.close();
}
}
/*
* Metodo che recupera la lista di tutti le persone rappresentanti dalle
* righe della tabella person
*/
@SuppressWarnings("unchecked")
public List<Person> getPersonsList() {
Session session = sessionFactory.openSession();
try {
Criteria criteria = session.createCriteria(Person.class);
return criteria.list();
} finally {
session.close();
}
}
/*
* Metodo che elimina dalla tabella person la riga avente uno specifico id
*
* @param l'id della persona da eliminare dalla tabella person
*/
@Transactional
public void delete(int id) {
Person personToDelete = getById(id);
sessionFactory.getCurrentSession().delete(personToDelete);
/*Session session = sessionFactory.openSession();
try {
Person personToDelete = getById(id);
System.out.println("person to delete: " + personToDelete);
session.delete(personToDelete);
} finally {
session.close();
}
*/
}
@Transactional
public void update(Person person){
sessionFactory.getCurrentSession().update(person);
/*
Session session = sessionFactory.openSession();
try {
System.out.println("UPDATING");
session.merge(person);
} finally {
System.out.println("CLOSE SESSION");
session.close();
}
*/
}
}
这个例子似乎运行正常(我使用包含main方法的主类测试了它,我在其中执行CRUD方法在表中插入行,查询单行或行列表,删除行并更新行中的值)
我唯一感到奇怪的是,要在删除和更新方法中正常工作,我必须从我的sessionFactory对象中获取当前会话< / strong>以这种方式:
sessionFactory.getCurrentSession().delete(personToDelete);
sessionFactory.getCurrentSession().update(person);
相反,当我必须添加或查询一行时,我必须以这种方式打开一个新会话:
Session session = sessionFactory.openSession();
为什么?
在之前的 PersonDAOImpl 课程中,我评论了删除和更新方法的旧实现,我试图打开一个新会话(因为我在addPerson和查询方法中没有问题但是这样做不起作用......只有当我得到当前会话才能正常工作
为什么呢?这个DAO对象的实现是否正确?
答案 0 :(得分:1)
一个好的起点是阅读精心编写的Hibernate documentation on transactions。明确指出SessionFactory.openSession()
和SessionFactory.getCurrentSession()
都可用于获取会话实例,但会话的获取方式不同。
使用SessionFactory.openSession()
,您迫使Hibernate根据您的需求打开新会话,这可能会导致意外/意外后果。相反,如果使用SessionFactory.getCurrentSession()
,则会从当前上下文收到Session
,这可能意味着交易,请求等。
为了更好地理解它,请考虑以下示例:您有一个服务层,需要从两个DAO对象调用方法,例如:
@Service
public class Service {
@Autowired
private FirstDao firstDao;
@Autowired
private SecondDao secondDao;
@Transactional
public void serviceMethod(int param) {
FirstEntity firstEntity = firstDao.getFirstEntity(param);
SecondEntity secondEntity = secondDao.updateSecondEntity(firstEntity);
}
}
您现在有两个选择:让每个DAO实施其Session session = factory.openSession();
或Session session = factory.getCurrentSession()
等方法。您的信念是两个方法调用(firstDao.getFirstEntity
和secondDao.updateSecondEntity
)都应该使用相同的会话,因为在firstEntity
方法调用之后,您的firstDao
至少处于分离状态完成(会话应该在那里关闭),你应该重新连接到新的会话以使代码工作。
但是当你在两个DAO方法中调用getCurrentSession
时,将返回相同的会话实例(因为事先由事务管理机制打开)。在这种情况下,Hibernate文档明确指出
您根本不需要使用Hibernate Transaction API与BMT或CMT,并且您可以自动传播绑定到事务的“当前”会话。
当您通过调用Session session = factory.getCurrentSession();
在DAO中获取会话实例时,所有会话管理都会发生,就是这样。 Hibernate文档的另一段摘录:
您的应用程序代码可以通过调用sessionFactory.getCurrentSession()来访问“当前会话”来处理请求。您将始终获得一个会话作用于当前数据库事务(italics mine)。
请注意Spring belobgs对服务/业务层的声明性事务划分,您可能需要从中调用几个使用相同Session
对象的DAO,该对象绑定到当前的交易背景。当调用标记为@Transactional
的方法并且提交事务并且在执行方法时关闭会话时,将启动事务并打开事务。
当然,这并不意味着每个事务会话是实现事务管理的唯一方法。还有其他概念,例如视图中的开放会话,会话每会话等。但在每种情况下,您的DAO都将从中获取Session
个对象当前的 context ,它由Container,Framework或您编写的代码管理。
关于您的代码,我建议修复以下不一致问题:
@Transactional
注释sessionFactory.getCurrentSession()
来操纵数据@Transactional
,这样Spring将为你管理会话和事务(否则寻找程序化事务划分在Hibernate文档中。相关材料:
答案 1 :(得分:0)
我会提供一些提示,但你需要深入研究源代码和文档。这里有很多概念。
在会话上执行某些操作并不意味着更改会立即推送到db。这在很大程度上取决于会话的刷新模式和您正在进行的操作。请参阅hibernate文档。刷新会话时,更改将推送到db。提交交易后,更改将持久。
如果你在关闭之前调用session.flush()
,在评论的代码中会有效,但由于下面的解释,这是不必要的。
在上面的更新和删除代码中,当调用'getCurrentSession'时,您要么获得现有会话,要么从org.springframework.orm.hibernate4.SpringJtaSessionContext
获取新会话(假设您正在使用JTA,再次深入了解代码以了解这一点)
创建新会话时,Hibernate注册与事务同步。在完成事务之前,Hibernate将回调。这里Hibernate会刷新会话。在您的情况下,春天通过您放置的注释划分的交易。
我建议你调用代码来调用'getCurrentSession',以便了解发生了什么。您将根据您的配置遇到这些类/方法。
org.hibernate.internal.SessionFactoryImpl.getCurrentSession()
org.springframework.orm.hibernate4.SpringJtaSessionContext.buildOrObtainSession()
org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization
org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorImpl.beforeCompletion()