0

我读过很多抱怨类似问题的帖子,但在我的情况下,它们都没有帮助我。无论如何,问题是我写了几个类:

个人实体:

@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 + " ]";
    }

}

个人道:

@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();
    }
}

抽象道:

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 方法 createPerson 的 DAO 包装器:

@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 中包含的配置一起工作:

数据源.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>

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

持久性.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>

接下来我做了一个 jUnit 测试来创建新的 Person 实例,并调用 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

所以我认为(经过一些研究) test env 默认情况下不允许对 db 进行任何更改(这将是非常合乎逻辑的)。所以我的第二次尝试是添加注释:

@Transactional
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)

在 PersonServiceTest 类上。结果就像在创建、提交和释放事务之前一样,没有任何错误。我再次进行了一些研究并制作了一个简单的控制器,并在那里放置了应该创建和持久化新的人实体的代码。

主控制器:

@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(类和方法级别)和控制器。

这种行为应该指出持久层配置的问题,但从数据库中选择数据工作正常。

此外,我还发现休眠在将数据写入数据库之前可能会有一些延迟,所以我在测试中添加了 thread.sleep(5000) 但它并没有太大变化。

也许我在配置中错过了一些东西,但我现在已经没有想法了。

我将不胜感激任何帮助。

4

3 回答 3

2

嗯,您的问题看起来很复杂,但是我意识到您在 Transactions.xml 上使用了 DatasourceTransactionManager,而您似乎正在使用 JPA 持久性。

也许尝试使用 JpaTransactionManager 代替。也许 DatasourceTransactionManager 没有为您提供使用 @Transactional 注释的方法的正确事务

于 2013-01-28T21:59:01.660 回答
0

您的数据库中的 Person Id 列是否允许为空?如果是这样,新创建的 Person 可能与现有行“相等”。这将导致更新,而不是插入。

于 2013-01-28T22:30:30.480 回答
0

在处理 ORM 映射时,您应该始终确保刷新您的EntityManager(或 Hibernate 会话,如果您使用它而不是 JPA)以避免在做出断言之前出现误报,例如

@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)
    }
}
于 2013-01-28T21:28:35.917 回答