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

}

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

}

この場合は@TransactionalメソッドcreatePersonを使用したDAOのラッパーである単純なPersonService :

@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テストを作成し、それをDAOと永続層に渡す必要があるPersonServiceのcreateメソッドを呼び出します。

テスト:

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への変更を許可しないと思いました(これは非常に論理的です)。したがって、2番目の試みは注釈を追加することでした。

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

PersonServiceTestクラス。結果は、トランザクションが作成、コミット、リリースされる前とまったく同じで、エラーは発生しませんでした。私はいくつかの調査を再度行い、単純なコントローラーを作成し、そこに新しい人物エンティティを作成して永続化するコードを配置しました。

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からのデータの選択は正常に機能します。

また、dbにデータを書き込む前に休止状態に多少の遅延が発生する可能性があるという意見に出くわしたので、テストでthread.sleep(5000)を追加しましたが、あまり変わりません。

たぶん私は構成で何かを逃したかもしれませんが、私は今アイデアを使い果たしました。

助けていただければ幸いです。

4

3 に答える 3

2

問題は複雑に見えますが、JPA永続性を使用しているように見えますが、Transactions.xmlでDatasourceTransactionManagerを使用していることに気付きました。

代わりにJpaTransactionManagerを使用してみてください。たぶん、DatasourceTransactionManagerは、@Transactionalで注釈を付けたメソッドに対して適切なトランザクションを提供していません。

于 2013-01-28T21:59:01.660 に答える
0

ORMマッピングを処理するときは、アサーションを作成する前に検知を回避するために、常に(または、JPAの代わりにHibernateセッションを使用している場合はHibernateセッションを)フラッシュする必要があります。EntityManager

@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 に答える
0

データベースのPersonId列でnullが許可されていますか?その場合、新しく作成されたPersonは既存の行と「等しい」可能性があります。これにより、挿入ではなく更新が発生します。

于 2013-01-28T22:30:30.480 に答える