Java 休眠@Version注解
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18982948/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Hibernate @Version annotation
提问by Senthil Muthiah
What is the relation between hibernate @version and ManyToOne Mapping.
hibernate @version 和 ManyToOne Mapping 之间的关系是什么。
Assume that i am having two tables Department and Employee. Here is Deparment is the master table and Employee in the detail table. In the Employee table, departmentID is reference as foreign key.
假设我有两个表部门和员工。这里 Deparment 是主表,而 Employee 是明细表。在Employee表中,departmentID作为外键被引用。
Here is my classes
这是我的课
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;
}
And also, Spring handles the session. So assume that, in one page, particular department is fetched and stored in the HTTP session.
而且,Spring 处理会话。因此,假设在一页中,特定部门被提取并存储在 HTTP 会话中。
Now in another page, i am trying to do the following
现在在另一个页面中,我正在尝试执行以下操作
Employee emp = new Employee();
emp.setName('Test')
emp.setDepartment(dept) // already stored in the HTTP session variable
service.save(emp)
Now i am getting the following exception
现在我收到以下异常
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing:
And just it make one change as follow and there is errror
只是它进行了如下更改,并且存在错误
Employee emp = new Employee();
emp.setName('Test')
dept.setVersion(0);
emp.setDepartment(dept) // already stored in the HTTP session variable
service.save(emp)
My Spring config
我的弹簧配置
<?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" />
Service Implementation
服务实施
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);
}
}
采纳答案by Alfredo Osorio
You need to first save the Department
before saving the Employee
.
您需要先保存Department
在保存之前Employee
。
service.save(dept);
service.save(emp);
UPDATEin response to your comment:
更新以回应您的评论:
In order to associate an Employee with a Department you need to have a Department that exists. Remember that in your database the Employee has a FK to the Department so what Hibernate is complaining about is that you are trying to save an Employee with a Department that does not exist, so you have these options:
为了将员工与部门相关联,您需要有一个存在的部门。请记住,在您的数据库中,员工对部门有一个 FK,所以 Hibernate 抱怨的是您试图用不存在的部门保存员工,因此您有以下选择:
- If the Department is a new Department you must save it first before saving the Employee.
- Find an already stored Department through a query such as entityManager.find(id, Department.class) and use that object in your Employee object.
- Mark as @Cascadeyour relationship with Deparment in the Employee.
回答by Francois
Your main problem is not so much the cascading but the fetching.
您的主要问题不是级联而是获取。
see the following post: Difference between FetchType LAZY and EAGER in Java Persistence API?
请参阅以下帖子: Java Persistence API 中 FetchType LAZY 和 EAGER 的区别?
@ManyToOne(fetch = FetchType.EAGER)
Otherwise as you mentionned your session gets closed and you loose the object. Eager fetching will ensure that it stays opened
否则,正如您所提到的,您的会话将关闭,并且您会丢失对象。Eager fetching 将确保它保持打开状态
回答by Lain
I know this is an old post, but maybe this will help others.
我知道这是一个旧帖子,但也许这会对其他人有所帮助。
This assumes you are using Hibernate as a JPA implementation.
这假设您使用 Hibernate 作为 JPA 实现。
Since your Department is stored in the session it's safe to say that it is detached.
由于您的部门存储在会话中,因此可以肯定地说它是分离的。
The best route for this case, since you are not modifying the Department instance is this:
这种情况的最佳路线,因为您没有修改 Department 实例是这样的:
Employee emp = new Employee();
emp.setName("Test");
emp.setDepartment(em.getReference(Department.class, dept.getID());
service.save(emp);
See Hibernate documentation here: Entity Manager - Loading an Object
请参阅此处的 Hibernate 文档:实体管理器 - 加载对象
If you get an EntityNotFoundException, make sure the code that originally retrieves Department calls at least one method on it within the transaction in which it is retrieved.
如果您收到 EntityNotFoundException,请确保最初检索 Department 的代码在检索它的事务中至少调用了一个方法。