【问题标题】:Spring, Hibernate, transaction management, not committedSpring,Hibernate,事务管理,未提交
【发布时间】:2012-05-16 15:04:00
【问题描述】:

所以,我尝试使用 Spring 进行事务管理。最后我的应用程序工作了,但删除不提交。在使用 Spring 之前,我在 DAO 中管理事务,我认为在我可以删除此代码之后。但现在删除不提交到数据库。

我的 DAO:

protected Session getSession() {
    Session session = sessionFactory.openSession();
    ThreadLocalSessionContext.bind(session);
    return session;
}
public void delete(T t) {
    Session session = getSession();
// **this approach I used before**
//  session.beginTransaction();
//  try {
//  session.delete(t);
//  session.getTransaction().commit();
//  } catch (Exception e) {
//      session.getTransaction().rollback();
//  }
    session.delete(t);
    }

我的应用上下文:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop 
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url"
            value="jdbc:mysql://ngs-java-srv.synapse.com:3306/mybase" />
        <property name="username" value="user" />
        <property name="password" value="password" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- <property name="configLocation" value="hibernate.cfg.xml" /> -->

        <property name="packagesToScan" value="todolist.entity" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!-- dao beans -->
    <bean id="userDao"
        class="todolist.dao.hibernate.UserDaoImpl">
        <constructor-arg>
            <value>todolist.entity.User</value>
        </constructor-arg>
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean id="itemDao"
        class="todolist.dao.hibernate.ItemDaoImpl">
        <constructor-arg>
            <value>todolist.entity.Item</value>
        </constructor-arg>
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- service bean -->
    <bean id="userService" class="todolist.service.UserServiceImpl">
        <property name="userDao" ref="userDao" />
        <property name="itemDao" ref="itemDao" />
    </bean>

    <!-- transaction manager -->
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

     <tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>

    <!-- <tx:annotation-driven/> -->

<!--    <aop:config>
        <aop:pointcut id="serviceMethods"
            expression="execution(* todolist.service.UserService.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
    </aop:config>
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRES_NEW" />
            <tx:method name="deleteItem" no-rollback-for="Exception" />
        </tx:attributes>
    </tx:advice> -->

    <!-- backbeans -->
    <bean id="userLogin" class="todolist.jsf.UserLogin"
        scope="request">
        <property name="userService" ref="userService" />
    </bean>

    <bean id="userLogged" class="todolist.jsf.UserLogged"
        scope="session">
        <aop:scoped-proxy />
    </bean>

    <bean id="userRegister" class="todolist.jsf.UserRegister"
        scope="request">
        <property name="userService" ref="userService" />
    </bean>

    <bean id="createItem" class="todolist.jsf.CreateItem"
        scope="request">
        <property name="userService" ref="userService" />
    </bean>

    <bean id="todoList" class="todolist.jsf.TodoList"
        scope="request">
        <property name="userService" ref="userService" />
    </bean>

</beans>

UserServiceImpl 类

package todolist.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import todolist.exception.AuthorizationError;
import todolist.exception.DuplicateLoginsException;
import todolist.service.StringToHashTool;
import todolist.dao.ItemDao;
import todolist.dao.UserDao;
import todolist.entity.Item;
import todolist.entity.User;

public class UserServiceImpl implements UserService {

    //private static final Logger log = Logger.getLogger(UserServiceImpl.class);

    private UserDao userDao;
    private ItemDao itemDao;

    public void setItemDao(ItemDao itemDao) {
    this.itemDao = itemDao;
    }

    public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean isUserExists(String login) {
    return (userDao.getUserByLogin(login) != null);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean isPasswordCorrect(String login, String password) {
    if (isUserExists(login)) {
        return userDao.getUserByLogin(login).getPassword()
            .equals(StringToHashTool.getHash(password));
    } else {
        return false;
    }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public User login(String login, String password) {
    if (isPasswordCorrect(login, password)) {
        return userDao.getUserByLogin(login);
    } else {
        throw new AuthorizationError("Incorrect password");
    }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean register(String login, String password) {
    if (isUserExists(login)) {
        throw new DuplicateLoginsException("Login " + login + " is already used.");
    } else {
        User user = new User();
        user.setLogin(login);
        user.setPassword(StringToHashTool.getHash(password));
        userDao.save(user);
        return true;
    }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logout() {

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Item createItem(Long creator_id, Long performer_id,
        String description, Date dueDate) {

    Item item = new Item();
    User user = userDao.getById(creator_id);

    item.setCreator(user);

    user = userDao.getById(performer_id);

    item.setPerformer(user);
    item.setDescription(description);
    item.setStartDate(new Date());
    item.setDueDate(dueDate);

    itemDao.save(item);

    return item;
    }

    @Override    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deleteItem(Long item_id) {
    Item item = itemDao.getById(item_id);
    itemDao.delete(item);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public List<Item> getUserItems(String login) {
    User user = userDao.getUserByLogin(login);
    return itemDao.getItemsByPerformer(user.getId());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public List<User> getUsers() {
    return userDao.getUsers();
    }
}

【问题讨论】:

    标签: java spring hibernate transactions


    【解决方案1】:
    <aop:pointcut 
        id="serviceMethods"
        expression="execution(*todolist.service.UserService.*(..))" 
    />
    

    第一步:

    将其更改为:

    <aop:pointcut 
        id="serviceMethods"
        expression="todolist.service.UserServiceImpl.delete()" 
    />
    

    查看删除是否开始提交。我只是想确保您清除所有拼写错误以及您有什么。如果此显式配置失败,则该配置一定有问题。

    【讨论】:

    • 你能提供全班吗? UserService 是什么接口?
    • 我的服务中有方法 deleteItem,它在 dao 中调用 delete,所以我写了 expression="(todolist.service.UserServiceImpl.deleteItem(..))",但得到了一个异常:错误::0 找不到引用的切入点 deleteItem。现在正在尝试修复。
    • 所以,我写了 expression="execution(* todolist.service.UserServiceImpl.deleteItem(..))",但是提交不起作用。
    【解决方案2】:

    我已经成功使用了 Spring HibernateTransactionManager 对象和注解驱动配置。在我的应用程序上下文中,我简单地声明了以下两个 bean:

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="session_factory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
    

    然后在我的服务层,你的 UserServiceImpl 例如:

    public class UserServiceImpl implements UserService {
    
    private final UserDao userDao;
    private final ItemDao itemDao;
    
    public UserServiceImpl(UserDao userDao, ItemDao itemDao) {
        this.userDao = userDao;
        this.itemDao = itemDao;
    }
    
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean isUserExists(String login) {
        return (userDao.getUserByLogin(login) != null);
    }
    
    ...
    

    将事务注释尽可能靠近视图(在 MVC 中)移动是很重要的。原因是 1) 性能和 2) 原子事务。如果调用 UserService 的调用在单个函数调用中调用 isUserExists、isPasswordCorrect 和 login 方法,您会希望它们在单个事务中执行。为此,请从 UserServiceImpl 中删除 @Transactional 注释并将其移至调用类。这确保了单个事务用于所有三个方法调用。对于原子执行,这也有效。想想经典的银行汇款示例。如果您在 UserService 上有两个单独的事务,则前两个方法调用可以成功,最后一个方法调用失败。前两个事务将被提交,因为它们位于单独的事务块中,这会使您的数据库处于不一致的状态。当然,原子事务主要与写入数据的事务相关。

    另外,不要将事务注释添加到接口,将它们保留在实现中。

    然后在我的 DAO 中,我只需执行 sessionFactory.getCurrentSession() 并从那里开始。 getCurrentSession() 是线程安全的,但对您的 DAO 保密。

    【讨论】:

    • 感谢您的回答。我根据您的建议进行更改。但是我的删除仍然没有提交。写入数据提交得很好。在我的 JSF-bean 中,我删除了循环中的项目,这可能是原因吗?
    【解决方案3】:

    在您的 DAO 中使用 getCurrentSession 而不是 openSession,因此 getSession 方法如下所示:

    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }
    

    您正在创建一个使用不同数据库连接的新会话,因此为服务设置的事务不适用。

    此外,当您配置它们时,DAO 将具有默认的单例范围;将实体作为构造函数参数传递是没有意义的。单例不应该具有特定于某个单独事务的实例状态。

    【讨论】:

    • 感谢您的帮助。当我更改我的 getSession() 时,我得到了下一个异常: createCriteria is not valid without active transaction in procedure from UserDaoImpl public User getUserByLogin(String login) { Session session = getSession(); System.out.println("会话为空:" + (session==null));条件标准 = session.createCriteria(User.class); criteria.add(Restrictions.eq("login", login));返回(用户)criteria.uniqueResult(); }
    • 当我改变我的getSession()时,我得到了下一个异常:createCriteria is not valid without active transaction in procedure from UserDaoImpl public User getUserByLogin(String login) { Session session = getSession();条件标准 = session.createCriteria(User.class);
    • 我从我的配置中删除了 thread 并且现在应用程序很好,但是当我尝试打开页面时,必须加载一些集合,a get 可以“不初始化代理 - 没有会话”
    • 还有!现在添加新记录效果很好,提交,所以你的评论非常有用,谢谢。
    猜你喜欢
    • 1970-01-01
    • 2012-10-16
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2011-04-30
    相关资源
    最近更新 更多