Spring + Hibernate とトランザクション アノテーションの使用。
私は以下をテストしようとしています:
- User オブジェクトを変更するメソッドを呼び出してから、
@Transactional
サービス メソッドを呼び出して永続化する - DB からオブジェクトを読み戻し、メソッドの後でその値が正しいことを確認します
私が抱えていた最初の問題は、ステップ 2 で User オブジェクトを読み取ったときに、Hibernate レベル 1 キャッシュ内のオブジェクトが返されただけで、実際にはデータベースから読み取られなかったことです。
したがって、セッションを使用してオブジェクトをキャッシュから手動で削除し、データベースからの読み取りを強制しました。ただし、これを行うと、オブジェクトの値は単体テスト内で永続化されません (指定した設定により、テストの完了後にロールバックすることがわかっています)。
サービスメソッドの呼び出し後にセッションを手動でフラッシュしようとしましたが@Transactional
、その DID が変更をコミットしました。しかし、それは私が期待したものではありませんでした。@Transactional
サービス メソッドを使用すると、トランザクションがコミットされ、セッションがフラッシュされてから返されることが保証されると思いました。一般に、Spring がこの管理をいつ行うかを決定することは知っていますが、メソッドの「作業単位」@Transactional
はそのメソッドだと思いました。
@Transactional
いずれにせよ、今は一般的にメソッドをテストする方法を理解しようとしています。
失敗しているjunitテストメソッドは次のとおりです。
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@TransactionConfiguration(transactionManager = "userTransactionManager", defaultRollback = true)
@WebAppConfiguration()
@ContextConfiguration(locations = { "classpath:test-applicationContext.xml",
"classpath:test-spring-servlet.xml",
"classpath:test-applicationContext-security.xml" })
public class HibernateTest {
@Autowired
@Qualifier("userSessionFactory")
private SessionFactory sessionFactory;
@Autowired
private UserService userService;
@Autowired
private PaymentService paymentService;
@Autowired
private QueryService queryService;
@Autowired
private NodeService nodeService;
@Autowired
private UserUtils userUtils;
@Autowired
private UserContext userContext;
@Test
public void testTransactions() {
// read the user
User user1 = userService.readUser(new Long(77));
// change the display name
user1.setDisplayName("somethingNew");
// update the user using service method that is marked @Transactional
userService.updateUserSamePassword(user1);
// when I manually flush the session everything works, suggesting the
// @Transactional has not flushed it at the end of the method marked
// @Transactional, which implies it is leaving the transaction open?
// session.flush();
// evict the user from hibernate level 1 cache to insure we are reading
// raw from the database on next read
sessionFactory.getCurrentSession().evict(user1);
// try to read the user again
User user2 = userService.readUser(new Long(77));
System.out.println("user1 displayName is " + user1.getDisplayName());
System.out.println("user2 displayName is " + user2.getDisplayName());
assertEquals(user1.getDisplayName(), user2.getDisplayName());
}
}
セッションを手動でフラッシュすると、テストは成功します。@Transactional
ただし、メソッドがセッションのコミットとフラッシュを処理することを期待していました。
updateUserSamePassword のサービス メソッドは次のとおりです。
@Transactional("userTransactionManager")
@Override
public void updateUserSamePassword(User user) {
userDAO.updateUser(user);
}
DAO メソッドは次のとおりです。
@Override
public void updateUser(User user) {
Session session = sessionFactory.getCurrentSession();
session.update(user);
}
SessionFactory は自動配線されています。
@Autowired
@Qualifier("userSessionFactory")
private SessionFactory sessionFactory;
XML アプリケーション コンテキスト構成を使用しています。私は持っている:
<context:annotation-config />
<tx:annotation-driven transaction-manager="userTransactionManager" />
と
<bean id="userDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${user.jdbc.driverClass}"/>
<property name="jdbcUrl" value="${user.jdbc.jdbcUrl}" />
<property name="user" value="${user.jdbc.user}" />
<property name="password" value="${user.jdbc.password}" />
<property name="initialPoolSize" value="3" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="17" />
</bean>
<bean id="userSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="userDataSource" />
<property name="configLocation" value="classpath:user.hibernate.cfg.xml" />
</bean>
<bean id="userTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="userDataSource" />
<property name="sessionFactory" ref="userSessionFactory" />
</bean>
サービスと dao クラスのコンポーネント スキャンもあります。私が言ったように、これは本番環境で機能しています。
そのメソッドの終わりまでにマークさ@Transactional
れたメソッドがある場合 (たとえば、ここでの更新メソッド)、Spring はセッションを強制的にコミットしてフラッシュするだろうと考えました。
いくつかのオプションしか表示されません。
これは一般的に機能していますが(単体テストではありません)、何かを誤って設定しました。推測はありますか?これをテストする方法についてのアイデアはありますか?
単体テスト構成自体に関する何かが、アプリのように動作していません。
トランザクションとセッションはそのようには機能しません。私の唯一の推測は、その更新メソッドを呼び出した後、Spring がトランザクションおよび/またはセッションを開いたままにしているということです。そのため、Session オブジェクトでユーザーを手動で削除しても、それらの変更はまだコミットされていません。
これが予想される動作であるかどうかは誰でも確認できますか? @Transaction
セッションでコミットとフラッシュを強制すべきではありませんか? そうでない場合、マークされたメソッドをどのようにテスト@Transactional
し、メソッドが実際にトランザクションで機能するかをテストしますか?
つまり、ここで単体テストをどのように書き直すべきですか?
他のアイデアはありますか?