关于在Spring应用程序中使用Hibernate删除和更新表行的一些疑问

时间:2013-02-24 12:18:50

标签: java spring hibernate spring-mvc

我在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对象的实现是否正确?

2 个答案:

答案 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.getFirstEntitysecondDao.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或您编写的代码管理。

关于您的代码,我建议修复以下不一致问题:

  • 从DAO图层实施中删除所有@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()