我已经阅读了很多线程抱怨类似的问题,但在我的案例中没有一个帮助我。无论如何,问题是我写了几个类:
人员实体:
@Entity
@Table(name="person")
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name="name")
private String name;
public Long getId() {
return id;
}
public Person setId(Long id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public Person setName(String name) {
this.name = name;
return this;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Person)) {
return false;
}
Person other = (Person) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "com.myhome.event_manager.entity.Person[ id=" + id + " ]";
}
}
的PersonDAO:
@Repository
public class PersonDAO extends AbstractDAO<Person> {
public PersonDAO() {
setClazz(Person.class);
}
public Person findByName(String name) {
return em.createQuery("WHERE p.name = :name", Person.class)
.setParameter("name", name)
.getSingleResult();
}
}
AbstractDao的:
public abstract class AbstractDAO<T extends Serializable> {
@PersistenceContext(unitName="pu-main")
protected EntityManager em;
protected Class< T> clazz;
public void setClazz(final Class< T> clazzToSet) {
this.clazz = clazzToSet;
}
public void setEm(EntityManager em) {
this.em = em;
}
public EntityManager getEm() {
return em;
}
public T findById(final Long id) {
return this.em.find(this.clazz, id);
}
public List< T> findAll() {
return this.em.createQuery("from " + this.clazz.getName())
.getResultList();
}
public void create(final T entity) {
this.em.persist(entity);
}
public void update(final T entity) {
this.em.merge(entity);
}
public void delete(final T entity) {
this.em.remove(entity);
}
public void deleteById(final Long entityId) {
final T entity = this.findById(entityId);
this.delete(entity);
}
}
和简单的 PersonService ,在这种情况下是带有@Transactional方法的DAO包装器createPerson:
@Service
public class PersonService {
@Autowired
private PersonDAO personDao;
@Transactional
public void createPerson(Person person) {
personDao.create(person);
}
public List<Person> listAllPersons() {
return personDao.findAll();
}
public Person getPersonWithName(String name) {
return personDao.findByName(name);
}
}
的pom.xml:
<properties>
<java-version>1.6</java-version>
<org.springframework-version>3.2.0.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<netbeans.hint.deploy.server>Tomcat</netbeans.hint.deploy.server>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!--Mysql driver-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.9</version>
</dependency>
<!--Hibernate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.9.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<!--JSTL-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
<type>jar</type>
</dependency>
</dependencies>
以上所有内容与root-context.xml中包含的配置一起使用:
DataSource.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!--MysQL - main-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL" />
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
<bean id="emFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
<property name="persistenceUnitName" value="pu-main" />
</bean>
Transactions.xml:
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!--enable the configuration of transactional behavior based on annotations-->
<tx:annotation-driven transaction-manager="txManager" />
<!--Transactions-->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
的persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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">
<persistence-unit name="pu-main" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source/>
<properties/>
</persistence-unit>
</persistence>
接下来我做了一个创建新Person实例的jUnit测试,并调用PersonService的create方法,该方法应该将它传递给DAO和持久层。
测试:
public class PersonServiceTest extends RootContextAwareTest {
@Autowired
private PersonService instance;
@Test
public void shouldAddOnePerson() {
logger.debug("TEST-shouldAddOnePerson");
//given
Person person = (new Person())
.setName("Test");
//when
logger.debug("BEFORE CREATE");
instance.createPerson(person);
logger.debug("AFTER CREATE");
//then
assertThat(instance.listAllPersons()).hasSize(6);
assertThat(instance.getPersonWithName("Test"));
}
}
RootContextAwareTest:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/config/spring/root-context.xml"})
public abstract class RootContextAwareTest {
protected static final Logger logger = LoggerFactory.getLogger(RootContextAwareTest.class);
}
实际上在person表中有5行,所以我只是在持久化新实体之后放置一个断言,检查它是否正确持久化。事实证明它不是,并且断言失败但令我惊讶的是,日志说一切顺利:
2013-01-28 21:28:07,148 - DEBUG: com.myhome.event_manager.test_base.RootContextAwareTest - TEST-shouldAddOnePerson
2013-01-28 21:28:07,148 - DEBUG: com.myhome.event_manager.test_base.RootContextAwareTest - BEFORE CREATE
2013-01-28 21:28:07,154 - DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name [com.myhome.event_manager.service.PersonService.createPerson]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2013-01-28 21:28:07,155 - DEBUG: org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/event_manager]
2013-01-28 21:28:07,172 - DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Acquired Connection [com.mysql.jdbc.JDBC4Connection@164a38ae] for JDBC transaction
2013-01-28 21:28:07,178 - DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@164a38ae] to manual commit
2013-01-28 21:28:07,208 - DEBUG: org.springframework.orm.jpa.EntityManagerFactoryUtils - Opening JPA EntityManager
2013-01-28 21:28:07,249 - DEBUG: org.springframework.orm.jpa.EntityManagerFactoryUtils - Registering transaction synchronization for JPA EntityManager
2013-01-28 21:28:07,267 - DEBUG: org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
2013-01-28 21:28:07,268 - DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction commit
2013-01-28 21:28:07,269 - DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@164a38ae]
2013-01-28 21:28:07,269 - DEBUG: org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@164a38ae] after transaction
2013-01-28 21:28:07,269 - DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
2013-01-28 21:28:07,270 - DEBUG: com.myhome.event_manager.test_base.RootContextAwareTest - AFTER CREATE
所以我认为(在一些研究之后)测试env默认情况下不允许对db进行任何更改(这是非常合乎逻辑的)。 所以我的第二次尝试是添加注释:
@Transactional
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
MainController:
@Controller
public class MainController {
private static final Logger logger = LoggerFactory.getLogger(MainController.class);
private ApplicationContext context = new ClassPathXmlApplicationContext("/config/spring/root-context.xml");
@Autowired
private PersonService ps;
@RequestMapping(method = RequestMethod.GET, value = "/")
public String index(Model model) {
Person person = (new Person())
.setName("Test-ąęć");
logger.debug("PERSIST PERSON");
ps.createPerson(person);
return "forward:/event/read";
}
}
结果与上述相同。
我还试过把@Transactional注释做DAO(类和方法级别)和控制器。
这种行为应该指出持久层配置的问题,但从数据库中选择数据工作正常。
此外,我发现在将数据写入db之前,hibernate可能会有一些延迟,所以我在测试中添加了thread.sleep(5000),但它并没有太大的改变。
也许我在配置上错过了一些东西,但我现在已经没想完了。
如果有任何帮助,我将不胜感激。
答案 0 :(得分:2)
嗯你的问题看起来很复杂,但是我意识到你在Transactions.xml上使用了DatasourceTransactionManager,而你似乎在使用JPA持久性。
也许尝试使用JpaTransactionManager。也许DatasourceTransactionManager没有为您使用@Transactional注释的方法提供正确的事务
答案 1 :(得分:0)
在处理ORM映射时,您应该始终确保刷新您的EntityManager
(如果您使用的是Hibernate会话而不是JPA),以避免false positives之前做出断言,例如
@Transactional
public class PersonServiceTest {
@PersistenceContext
EntityManager em;
@Test
public void someTest() {
// given (initialization)
// when (write stuff to be persisted)
em.flush();
// then (read and validate)
}
}
答案 2 :(得分:0)
数据库中的Person Id列是否允许为空?如果是这样,新创建的Person可能与现有行“相等”。这会导致更新,而不是插入。