hibernate @version和ManyToOne Mapping之间的关系是什么。
假设我有两个表部门和员工。这里是Deparment的主表 和详细信息表中的员工。在Employee表中,departmentID作为外键引用。
这是我的课程
Public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long ID;
@Version
private Long version;
//Getters and Setters
}
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long ID;
@Version
private Long version;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "departmentID" )
private Department department;
}
而且,Spring处理会话。因此,假设在一个页面中,提取并存储特定部门 在HTTP会话中。
现在在另一页中,我正在尝试执行以下操作
Employee emp = new Employee();
emp.setName('Test')
emp.setDepartment(dept) // already stored in the HTTP session variable
service.save(emp)
现在我收到以下异常
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing:
只是它做了一个改变如下,并且有错误
Employee emp = new Employee();
emp.setName('Test')
dept.setVersion(0);
emp.setDepartment(dept) // already stored in the HTTP session variable
service.save(emp)
我的春天配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- Container Configuration: The IOC container configuration xml file is
shown below,The container has the <context:component-scan> element and <context:annotation-config/>
<context:annotation-config/> used to intimate the beans of this IOC container
are annotation supported. By pass the base path of the beans as the value
of the base-package attribute of context:component-scan element, we can detect
the beans and registering their bean definitions automatically without lots
of overhead. The value of base-package attribute is fully qualified package
name of the bean classes. We can pass more than one package names by comma
separated -->
<context:annotation-config />
<context:component-scan base-package="com.product.business" />
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- This will ensure that hibernate or jpa exceptions are automatically
translated into Spring's generic DataAccessException hierarchy for those
classes annotated with Repository -->
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="CRUDService" class="com.product.business.service.CRUDServiceImpl" />
<bean id="AuthService" class="com.product.business.service.AuthServiceImpl" />
服务实施
package com.product.business.service;
import java.io.Serializable;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.product.business.dao.CRUDDao;
@Service
public class CRUDServiceImpl implements CRUDService {
@Autowired
private CRUDDao CRUDDao;
@Transactional(readOnly = true)
public <T> List<T> getAll(Class<T> klass) {
return CRUDDao.getAll(klass);
}
@Transactional
public <T> void Save(T klass) throws DataAccessException {
CRUDDao.Save(klass);
}
@Transactional
public <T> void delete(T klass) throws DataAccessException {
CRUDDao.delete(klass);
}
@Transactional
public <T> T GetUniqueEntityByNamedQuery(String query, Object... params) {
return CRUDDao.GetUniqueEntityByNamedQuery(query, params);
}
@Transactional
public <T> List<T> GetListByNamedQuery(String query, Object... params) {
return CRUDDao.GetListByNamedQuery(query, params);
}
@Override
@Transactional(readOnly = true)
public <T> Long getQueryCount(String query, Object... params) {
return CRUDDao.getQueryCount(query, params);
}
@Override
@Transactional(readOnly = true)
public <T> T findByPrimaryKey(Class<T> klass, Serializable id) {
return CRUDDao.findByPrimaryKey(klass, id);
}
}
答案 0 :(得分:2)
您需要先保存Department
,然后再保存Employee
。
service.save(dept);
service.save(emp);
更新以回复您的评论:
为了将员工与部门相关联,您需要拥有一个存在的部门。请记住,在你的数据库中,Employee对部门有一个FK,所以Hibernate抱怨的是你试图用一个不存在的部门来保存一个员工,所以你有这些选择:
答案 1 :(得分:0)
你的主要问题不在于级联,而在于获取。
见以下帖子: Difference between FetchType LAZY and EAGER in Java Persistence API?
@ManyToOne(fetch = FetchType.EAGER)
否则,当您提到您的会话被关闭并且您松开了该对象时。急切的提取将确保它保持打开状态
答案 2 :(得分:0)
我知道这是一个老帖子,但也许这会对其他人有所帮助。
这假设您使用Hibernate作为JPA实现。
由于您的部门存储在会话中,因此可以安全地说它已被分离。
这种情况的最佳途径,因为您没有修改Department实例,所以:
Employee emp = new Employee();
emp.setName("Test");
emp.setDepartment(em.getReference(Department.class, dept.getID());
service.save(emp);
请参阅此处的Hibernate文档:Entity Manager - Loading an Object
如果您收到EntityNotFoundException,请确保最初检索Department的代码在检索它的事务中至少调用一个方法。