-1

我在 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();
        }
        */
    }

}

这个例子似乎工作正常(我已经使用包含一个主方法的主类对其进行了测试,在该主类中我执行 CRUD 方法在表中插入行,查询单行或行列表,删除一行并更新连续值)

我唯一觉得奇怪的是,要在删除更新方法中正常工作,我必须以这种方式从我的 sessionFactory 对象中获取当前会话

sessionFactory.getCurrentSession().delete(personToDelete);
sessionFactory.getCurrentSession().update(person);

相反,当我必须添加或查询一行时,我必须以这种方式打开一个新会话

Session session = sessionFactory.openSession();

为什么?

在之前的PersonDAOImpl类中,我已经评论了删除更新方法的旧实现,我试图打开一个新会话(就像我在 addPerson 和查询方法中所做的那样没有问题)但是这样不起作用......工作仅当我获得当前会话时才可以

为什么?DAO 对象的这个实现是否正确?

4

2 回答 2

1

一个好的起点是阅读编写良好的Hibernate 关于事务的文档。明确说明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();or Session session = factory.getCurrentSession()。您的信念是,两个方法调用(firstDao.getFirstEntitysecondDao.updateSecondEntity)都应该使用同一个会话,因为至少在方法调用完成firstEntity后您处于分离状态firstDao(会话应该在那里关闭),您应该将其重新附加到新会话以使代码工作。

但是,当您getCurrentSession在两个 DAO 方法中调用时,将返回相同的会话实例(因为它是由事务管理机制预先打开的)。在这种情况下,Hibernate 文档明确指出

您根本不需要将 Hibernate Transaction API 与 BMT 或 CMT 一起使用,并且您可以自动传播绑定到事务的“当前”会话。

当您通过调用在 DAO 中获取会话实例时,您的所有会话管理都会发生Session session = factory.getCurrentSession();,仅此而已。Hibernate 文档的另一个摘录:

您的应用程序代码可以通过调用 sessionFactory.getCurrentSession() 访问“当前会话”来处理请求。您将始终获得一个Session 范围为当前数据库事务(斜体我的)。

请注意, Spring 对服务/业务层的声明式事务划分,您可能需要从中调用多个使用相同Session对象的 DAO,该对象绑定到当前事务上下文。当标记的方法@Transactional被调用并提交事务时,会话打开,事务启动,方法执行时会话关闭。

当然,这并不意味着每事务会话是实现事务管理的唯一方法。还有其他概念,例如Open session in viewSession-per-conversation等。但在每种情况下,您的 DAO 都会Session从当前上下文中获取一个对象,该对象又由容器、框架或您的编写代码管理。

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

  • 从 DAO 层实现中删除所有@Transactional注释
  • sessionFactory.getCurrentSession()用于操作数据
  • 寻找一个考虑事务划分的服务层:你的方法应该被我标记为@Transactional,这样 Spring 将为你管理会话和事务(否则在 Hibernate 文档中查找程序化事务划分。

相关资料:

于 2013-02-25T10:07:39.493 回答
0

我会给出一些提示,但您需要深入研究源代码和文档。这里有很多概念。

对 session 进行一些操作并不意味着更改会立即推送到 db。这在很大程度上取决于您正在执行的会话和操作的刷新模式。请参阅有关此的休眠文档。刷新会话时将更改推送到 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()
于 2013-02-24T18:55:09.073 回答