我花了两天时间试图解决一个奇怪的问题。我在 MVC Spring App 中工作,当我运行以下测试时......
@ContextConfiguration(locations = "/persistence-beans.xml")
public class UserDaoTest extends AbstractJUnit4SpringContextTests{
@Autowired
private UserDao userDao;
@Test
public void testAdd() {
int size = userDao.list().size();
User user = new User();
user.setName("omar");
userDao.add(user);
List<User> users = userDao.list();
System.out.println("users size: " + users.size());
// list should have one more employee now
assertTrue (size < users.size());
}
}
我在数据库中看到了结果,一切正常。但是,当我调用控制器并使用相同的 Dao (userDao) 创建用户时,应用程序不会持久化用户并且不会抛出任何错误。我唯一想到的是 servlet-context.xml 部分中来自 Spring 的事务注释存在问题。我可以相信在测试用例中有效,但在应用程序流中无效!拜托,我需要摆脱这个问题!!!!
------ 接口道 --------
public interface GenericDao<E, K> {
void add(E entity);
void update(E entity);
void remove(E entity);
E find(K key);
List<E> list();
}
public interface UserDao extends GenericDao<User, Long>{
}
package com.tutorial.dao.impl;
import java.io.Serializable;
import java.util.List;
import com.tutorial.dao.GenericDao;
import java.lang.reflect.ParameterizedType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public class GenericDaoJPAImpl<E, K extends Serializable> implements GenericDao<E, K>{
@PersistenceContext
private EntityManager entityManager;
protected Class<E> daoType;
@SuppressWarnings("unchecked")
public GenericDaoJPAImpl() {
daoType = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
public void add(E entity) {
em().persist(entity);
}
public EntityManager em() {
return entityManager;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public void update(E entity) {
em().merge(entity);
}
@Override
public void remove(E entity) {
em().remove(em().merge(entity));
}
@Override
public E find(K key) {
return (E) em().find(daoType, key);
}
@Override
public List<E> list() {
List<E> list = null;
TypedQuery<E> query = em().createQuery("select o from "
+ daoType.getSimpleName() + " o",
daoType);
list = query.getResultList();
return list;
}
}
@Repository("UserDao")
public class UserDaoImpl extends GenericDaoJPAImpl<User, Long> implements UserDao{
}
-------- 服务层 ------------
@Service("calculatorService")
@Transactional(propagation= Propagation.REQUIRED, readOnly=false)
public class CalculatorServiceImpl implements CalculatorService{
@Autowired
private UserDao userDao;
@Override
public void createUser(User user) {
userDao.add(user);
}
}
- - - - - 控制器 - - - - - - -
@Controller
@RequestMapping("/calculate")
public class CalculatorController {
@Autowired
private CalculatorService calculatorService;
@RequestMapping(method = RequestMethod.POST)
public String addUser(User user){
calculatorService.createUser(user);
return "session";
}
}
-------- 持久性 Bean ---------------------------------
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- we can use annotations -->
<context:annotation-config />
<!-- we will manage transactions with annotations -->
<tx:annotation-driven />
<context:component-scan base-package="com.tutorial.dao.impl" />
<context:property-placeholder location="classpath*:properties/database.properties"/>
<!-- data source for our database -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- EntityManagerFactoryBean -->
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
---------------------- Servlet 上下文 -------------- ---
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<!--All beans are scanned here, except the Repository because they are scanned in persistence-beans.xml-->
<context:component-scan base-package="com.tutorial.*" >
<context:exclude-filter expression="org.springframework.stereotype.Repository" type="annotation"/>
</context:component-scan>
</beans:beans>
------------------------------------ WEB XML ------------- --------
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
classpath:persistence-beans.xml
</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>calculator</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>calculator</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
这个项目的链接是下一个:https ://github.com/igiagante/calculator
随意下载以证明发生了什么:) 谢谢!