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

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

问题描述

我使用的是Spring + Hibernate + JPA,我有一种情况是我无法让我的实体持久化到数据库。我已经建立了一个服务类,它是用@Transactional注释的。它使用包含注入的EntityManager的DAO。当我调用服务对象上的函数时,我看到DAO正在执行的读操作的一堆选择,但没有更新/删除,因为我的DAO发布了合并和删除操作。当然,我的设置有问题,但我看不到它。



persistence.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>
<属性>
< property name =hibernate.dialectvalue =org.hibernate.dialect.InformixDialect/>
< property name =hibernate.cache.provider_classvalue =org.hibernate.cache.NoCacheProvider/>
< property name =hibernate.showsqlvalue =true/>
< property name =hibernate.cache.use_second_level_cache
value =false/>
< / properties>
< / persistence-unit>

config.xml

 < 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/securit y / 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 =dataSourceclass =org.apache.commons.dbcp.BasicDataSource
destroy-method =close>
< property name =driverClassNamevalue =org.postgresql.Driver/>
< property name =urlvalue =jdbc:postgresql:// localhost:5432 / testdb/>
< property name =usernamevalue =username/>
< property name =passwordvalue =password/>
< / bean>

< bean id =entityManagerFactory
class =org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean>
< property name =persistenceUnitNamevalue =pu/>
< property name =dataSourceref =dataSource/>
< property name =jpaVendorAdapter>
< bean class =org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter>
< property name =showSqlvalue =true/>
< property name =databasePlatformvalue =org.hibernate.dialect.PostgreSQLDialect/>
< / bean>
< / property>
< property name =loadTimeWeaver>
< bean
class =org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver/>
< / property>
< / bean>

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

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

< tx:注解驱动的事务管理器=事务管理器proxy-target-class =true/>

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

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);

客户端客户端= 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;


$ / code $ / pre

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 , 激活码);

PendingActivation pendingActivation = null;
尝试{
pendingActivation =(PendingActivation)q.getSingleResult();
返回pendingActivation;

catch(Exception e){
log.warn(无法检索激活码+ activationCode +for user+ username,e);
返回null;



@Override
public boolean enableUser(Client client){

try {
client.setEnabled(真正);
client = em.merge(client); //不会产生更新

catch(Exception e){
log.error(Unable to enable client:+ client.getUsername(),e);
返回false;
}

返回true; $ Pe

$ b @Override
public boolean removePendingActivation(PendingActivation pendingActivation){
try {
pendingActivation =(PendingActivation)em.getReference(PendingActivation.class, pendingActivation.getPendingActivationId());
em.remove(pendingActivation); //不会产生删除

catch(Exception e){
log.warn(Unable to remove activation:+ pendingActivation.getActivationCode(),e);
返回false;
}

返回true;


AccountActivationController.java

  @Controller 
public class AccountActivationController {
$ b $ @Autowired
@Qualifier(accountService)
AccountService accountService;

@RequestMapping(activate.do)
public String doActivate(
@RequestParam(activationCode)字符串activationCode,
@RequestParam(username)字符串用户名,
ModelMap模型){

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

if(unitCriteria == null){
returnaccount / activationError;
}

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

}


解决方案

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

Spring文档声明以下内容:


< tx:annotation-driven /> 只会寻找$
这意味着,如果你把
< tx:注解驱动的/ >
WebApplicationContext中为
DispatcherServlet,它仅检查
控制器中的
@Transactional bean,而不是您的服务。
有关更多
信息,请参阅第15.2节
DispatcherServlet。

在我原来的文章中是我的servlet定义,它有以下几行配置代码:
$ b $ myServlet.xml

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

这会将所有带注释的bean(包括Controllers,Services和Repositories)带入servlet上下文中,而不是应用程序上下文。问题在于此。当Spring查找用@Transactional注释的bean(由于在我的config.xml文件中存在< tx:annotation-driven /> ),它正在寻找它们在应用程序上下文中。并且,基于我在之前的线程中发布的配置,没有bean被加载到我的应用程序上下文中......它们都在servlet上下文中。因此,当我的servlet调用用@Service&标注的bean时, @Transactional是使用未被事务代理包装的bean 。因此,没有交易。诀窍(相反,正确的方法)是通过以下方式更改我的配置文件:

myServlet.xml

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

config.xml

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

该配置可确保所有控制器都存在于servlet上下文中,并且Transactional Services和Repositories存在于应用程序上下文中,是他们所属的地方。最后,经过许多不眠之夜,我的数据库写入仍然存在。


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.

persistence.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>

config.xml

<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

@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

@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

@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.

The Spring documentation states the following:

<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.

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

myServlet.xml

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

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

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

config.xml

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

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天全站免登陆