实体不持久 - Spring + Hibernate + JPA [英] Entities Not Persisting - Spring + Hibernate + JPA

查看:21
本文介绍了实体不持久 - Spring + Hibernate + JPA的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Spring + Hibernate + JPA,但我遇到了无法让我的实体持久保存到数据库的情况.我已经设置了一个用@Transactional 注释的服务类.它使用包含注入的 EntityManager 的 DAO.当我在服务对象上调用该函数时,我看到 DAO 正在执行的读取的一堆选择,但由于我的 DAO 发出的合并和删除没有更新/删除.我的设置肯定有问题,但我看不到.

I'm using Spring + Hibernate + JPA and I have a situation where I can't get my entities to persist to the database. I've set up a service class that is annotated with @Transactional. It uses a DAO that contains an injected EntityManager. When I call the function on the service object I see a bunch of selects for the reads the DAO is doing, but no updates/deletes as a result of merges and removes issued by my DAO. Surely there's something wrong with my setup, but I can't see it.

持久性.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">
    <persistence-unit name="pu">
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.InformixDialect" />
            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider" />
            <property name="hibernate.showsql" value="true" />
            <property name="hibernate.cache.use_second_level_cache"
                value="false" />
        </properties>
    </persistence-unit>

配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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
                http://www.springframework.org/schema/security
                http://www.springframework.org/schema/security/spring-security-3.0.3.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://localhost:5432/testdb" />  
        <property name="username" value="username" />
        <property name="password" value="password" />
    </bean>

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="pu" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
            </bean>
        </property>
        <property name="loadTimeWeaver">
            <bean
                class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
        p:entityManagerFactory-ref="entityManagerFactory" />

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

    <context:annotation-config/>
    </beans>

AccountService.java

AccountService.java

@Service("accountService")
@Transactional(propagation=Propagation.REQUIRED)
public class AccountService {

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

    @Autowired
    private UserDAO userDAO;

    public void activateUser(String username, String activationCode) {
        PendingActivation pendingActivation = userDAO.getPendingActivation(
                username, activationCode);

            Client client = pendingActivation.getClient();

            if (!userDAO.removePendingActivation(pendingActivation)) {
                log.warn("Unable to remove pending activation");
            }

            if (!userDAO.enableUser(client)) {
                log.error("Unable to enable client");
                return;
            }

            return;
        }
    }

UserDAOImpl.java

UserDAOImpl.java

@Repository("userDAO")
public class UserDAOImpl implements UserDAO, Serializable {

    private static final long serialVersionUID = 1L;

    private static Logger log = Logger.getLogger(UserDAOImpl.class);

    @PersistenceContext
    EntityManager em;

    @Override 
    public PendingActivation getPendingActivation(String username, String activationCode) {
        Query q = em.createNamedQuery("getActivationCode")
            .setParameter("activationCode", activationCode);

        PendingActivation pendingActivation = null;
        try {
            pendingActivation = (PendingActivation)q.getSingleResult();
            return pendingActivation;
        }
        catch (Exception e) {
            log.warn("Could not retrieve activation code " + activationCode + " for user " + username, e);
            return null;
        }
    }

    @Override
    public boolean enableUser(Client client) {

        try {
            client.setEnabled(true);
            client = em.merge(client);   // this never generates an update
        }
        catch(Exception e) {
            log.error("Unable to enable client: " + client.getUsername(), e);
            return false;
        }

        return true;
    }

    @Override
    public boolean removePendingActivation(PendingActivation pendingActivation) {
        try {
            pendingActivation = (PendingActivation)em.getReference(PendingActivation.class, pendingActivation.getPendingActivationId());
            em.remove(pendingActivation);  // this never generates a delete
        }
        catch(Exception e) {
            log.warn("Unable to remove activation: " + pendingActivation.getActivationCode(), e);
            return false;
        }

        return true;
    }
}

AccountActivationController.java

AccountActivationController.java

@Controller
public class AccountActivationController {

    @Autowired
    @Qualifier("accountService")
    AccountService accountService;

    @RequestMapping("activate.do")
    public String doActivate(
            @RequestParam("activationCode") String activationCode,
            @RequestParam("username") String username,
            ModelMap model) {

        UnitCriteria unitCriteria = accountService.activateUser(username, activationCode);

        if (unitCriteria == null) {
            return "account/activationError";
        }

        model.addAttribute("fromActivation", true);
        return "forward:search.do?" + unitCriteria.toUrlParams(true);
    }

}

推荐答案

好的,我找到了问题所在.我花了很长时间才弄明白,与我的数据库配置无关,所以我想帮助有类似问题的人.

Ok, I figured out the problem. It took me forever to figure it out and had nothing to do with my database config, so I want to help people who have similar issues.

Spring 文档声明如下:

The Spring documentation states the following:

只查找@Transactional 在同一个 bean 上它是在应用程序上下文中定义的.这意味着,如果你把 在一个WebApplicationContext 的DispatcherServlet,它只检查@Transactional bean 在你的控制器,而不是您的服务.见第 15.2 节,DispatcherServlet"了解更多信息.

<tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services. See Section 15.2, "The DispatcherServlet" for more information.

我原帖里没有贴的是我的servlet定义,里面有以下几行配置代码:

What's not posted in my original post is my servlet definition, which has the following lines of configuration code:

myServlet.xml

myServlet.xml

<context:annotation-config /> 
<context:component-scan base-package="com.myDomain.*" /> 

这将所有带注释的 bean,包括控制器、服务和存储库,带入 servlet 上下文而不是应用程序上下文.这就是问题所在.当 Spring 查找用 @Transactional 注释的 bean 时(由于在我的 config.xml 文件中存在 <tx:annotation-driven/>),它正在应用程序上下文中查找它们.而且,根据我在上一个线程中发布的配置,没有 bean 被加载到我的应用程序上下文中……它们都在 servlet 上下文中.因此,当我的 servlet 调用用 @Service & 注释的 bean 时,@Transactional 使用没有被事务代理包装的 bean.因此,没有交易.诀窍(更确切地说,正确的方法)是按以下方式更改我的配置文件:

This brings all annotated beans, including Controllers, Services and Repositories, into the servlet context instead of the application context. And therein lies the problem. When Spring looks for beans annotated with @Transactional (due to the existence of <tx:annotation-driven/> in my config.xml file) it is looking for them in the application context. And, based on my config that was posted in my previous thread, there are no beans being loaded into my application context... they're all in the servlet context. Therefore, when my servlet was calling the beans annotated with @Service & @Transactional it was using beans that were not wrapped by transaction proxies. Thus, no transactions. The trick (rather, the correct way) was to change my config files in the following way:

myServlet.xml

myServlet.xml

<context:annotation-config /> 
<context:component-scan base-package="com.myDomain.servlets" /> 

配置文件

<context:annotation-config /> 
<context:component-scan base-package="com.myDomain.dao" /> 
<context:component-scan base-package="com.myDomain.services" /> 

此配置确保所有控制器都存在于 servlet 上下文中,而事务服务和存储库存在于应用程序上下文中,这是它们所属的地方.最后,在无数个不眠之夜之后,我的数据库写入仍在持续.

This configuration ensures that all Controllers exist in the servlet context and Transactional Services and Repositories exist in the application context, which is where they belong. And finally, after many sleepless nights, my database writes are persisting.

这篇关于实体不持久 - Spring + Hibernate + JPA的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆