java - Spring + JPA 事务已创建并提交,但数据库中没有结果

标签 java spring hibernate jpa transactions

我读过很多提示类似问题的帖子,但没有一个对我的情况有帮助。不管怎样,问题是我写了几个类:

人实体:

@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,在本例中它是 DAO 的包装器,带有 @Transactional 方法 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>

接下来我做了一个 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

所以我认为(经过一些研究)测试环境默认不允许对数据库进行任何更改(这是非常合乎逻辑的)。 所以我的第二次尝试是添加注释:

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

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

主 Controller :

@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(类和方法级别)和 Controller 上。

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

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

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

如有任何帮助,我将不胜感激。

最佳答案

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

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

关于java - Spring + JPA 事务已创建并提交,但数据库中没有结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14571113/

相关文章:

java - JDBC并发实现

java - JTable打印不打印页眉页脚

java - 从 Spring 应用程序上下文获取 bean 时出现 ClassCastException,但在 Autowiring 时则不会出现 ClassCastException

使用 eclipse 或 STS 的 Spring Batch

java - Hibernate - 使用注释映射三个表

java - 使用 Hibernate 持久化父类(super class)类型的一对多列表

java - 在java中将字符串解析为 boolean 值

java - 删除空格和前导逗号

java - 如何创建多对多连接,其中所有者实体可以拥有多个拥有的实体?

Spring-data-jpa 渴望获取加入和使用分页不起作用