为什么 getHibernateTemplate().delete(entity) 失败?
下面是代码:
// Hibernate 模型
@Entity
@Table(name="contact")
public class Contact {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
@Column(name = "contact_id")
private Long id;
@Column(name="contact_ref_id")
private String contactRefId;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@ManyToOne
@JoinColumn(name="app_user_id")
/** user whose contact list owns this contact */
private AppUser appUser;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
/**
* @return AppUser who owns contact list this contact belongs to
*/
public AppUser getAppUser() {
return appUser;
}
public void setAppUser(AppUser appUser) {
this.appUser=appUser;
}
public String getContactRefId() {
return contactRefId;
}
public void setContactRefId(String contactRefId) {
this.contactRefId=contactRefId;
}
}
// DAO 层
private Criteria createCriteria() {
return getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(Contact.class);
}
public void deleteContact(String contactRefId) {
Criteria criteria = createCriteria();
criteria.add(Restrictions.eq("contactRefId", contactRefId));
Contact contact = (Contact)criteria.uniqueResult();
try {
contact = getHibernateTemplate().merge(contact);
getHibernateTemplate().delete(contact);
} catch (DataAccessException e) {
log.error(e.getMessage());
throw e;
}
}
// 服务层
public void deleteContact(String contactRefId) {
contactDao.delete(contactRefId);
}
// 单元测试
@Test
public void testDeleteContact() {
contactService.deleteContact("fe43b43a-d77f-45ce-b024-bb6e93264a69");
}
// Spring 配置
<beans>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="model.hibernate"/>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
hibernate.query.substitutions=true 'Y', false 'N'
hibernate.cache.use_second_level_cache=true
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
</value>
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="contactsDao" class="ContactDaoHibernate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* service..*.*(..))" order="0"/>
</aop:config>
<!-- Enable @Transactional support -->
<tx:annotation-driven/>
<!-- Enable @AspectJ support -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
</beans>
当我触发测试方法时,不会引发异常,但数据库中的行不会被删除。通过 criteria.uniqueResult() 从数据库获取联系人对象后,在调试器中检查该对象会发现已获取完整且正确的对象。尽管合并调用可能看起来是不必要的,但我这样做是为了看看是否存在用于删除的错误会话的问题。
因此,尽管 Spring 配置很简单并且我没有看到任何潜在问题,但事务中并未调用删除。最令人困惑的是这个 DAO 代码是否有效:
public void addContact(Contact contact) {
try {
getHibernateTemplate().save(contact);
} catch (DataAccessException e) {
log.error(e.getMessage());
throw e;
}
}
我没有主意了。
Here is the code:
// Hibernate model
@Entity
@Table(name="contact")
public class Contact {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
@Column(name = "contact_id")
private Long id;
@Column(name="contact_ref_id")
private String contactRefId;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@ManyToOne
@JoinColumn(name="app_user_id")
/** user whose contact list owns this contact */
private AppUser appUser;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
/**
* @return AppUser who owns contact list this contact belongs to
*/
public AppUser getAppUser() {
return appUser;
}
public void setAppUser(AppUser appUser) {
this.appUser=appUser;
}
public String getContactRefId() {
return contactRefId;
}
public void setContactRefId(String contactRefId) {
this.contactRefId=contactRefId;
}
}
// DAO layer
private Criteria createCriteria() {
return getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(Contact.class);
}
public void deleteContact(String contactRefId) {
Criteria criteria = createCriteria();
criteria.add(Restrictions.eq("contactRefId", contactRefId));
Contact contact = (Contact)criteria.uniqueResult();
try {
contact = getHibernateTemplate().merge(contact);
getHibernateTemplate().delete(contact);
} catch (DataAccessException e) {
log.error(e.getMessage());
throw e;
}
}
// Service layer
public void deleteContact(String contactRefId) {
contactDao.delete(contactRefId);
}
// Unit test
@Test
public void testDeleteContact() {
contactService.deleteContact("fe43b43a-d77f-45ce-b024-bb6e93264a69");
}
// Spring config
<beans>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="model.hibernate"/>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
hibernate.query.substitutions=true 'Y', false 'N'
hibernate.cache.use_second_level_cache=true
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
</value>
</property>
</bean>
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="contactsDao" class="ContactDaoHibernate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* service..*.*(..))" order="0"/>
</aop:config>
<!-- Enable @Transactional support -->
<tx:annotation-driven/>
<!-- Enable @AspectJ support -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
</beans>
When I fire the test method, no exceptions are thrown, but the row in the db is not deleted. Examination of the Contact object in the debugger after it's fetched from the db by criteria.uniqueResult() reveals the complete and correct object was fetched. Although the merge call may appear unneccesary, I did that to see if there was an issue w/ the wrong Session being used for the delete.
It appears therefore that the the delete is not being called in a transaction, although the Spring config is simple and I don't see any potential issues. What is most perplexing is that this DAO code works:
public void addContact(Contact contact) {
try {
getHibernateTemplate().save(contact);
} catch (DataAccessException e) {
log.error(e.getMessage());
throw e;
}
}
I'm out of ideas.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
强烈建议为您的对象实现
equals()
和hashCode()
,因为您似乎对每个操作使用单独的会话。请参阅 http:// docs.jboss.org/hibernate/core/3.3/reference/en/html/persistent-classes.html#persistent-classes-equalshashcode
更新
看来您的基于 Spring 的单元测试不提交事务。默认情况下,Spring 测试框架将回滚它启动的事务。
It's highly recommended to implement
equals()
andhashCode()
for your object since you seem to use separate sessions for each operation.See http://docs.jboss.org/hibernate/core/3.3/reference/en/html/persistent-classes.html#persistent-classes-equalshashcode
Update
It appears that your Spring based unit test does not commit transaction. By default Spring test framework will rollback transaction that it started.