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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 13:03:42  来源:igfitidea点击:

Hibernate @Version annotation

javahibernatehibernate-mappingspring-orm

提问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 Departmentbefore 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 抱怨的是您试图用不存在的部门保存员工,因此您有以下选择:

  1. If the Department is a new Department you must save it first before saving the Employee.
  2. Find an already stored Department through a query such as entityManager.find(id, Department.class) and use that object in your Employee object.
  3. Mark as @Cascadeyour relationship with Deparment in the Employee.
  1. 如果部门是新部门,您必须在保存员工之前先保存它。
  2. 通过诸如 entityManager.find(id, Department.class) 之类的查询查找已存储的部门,并在您的 Employee 对象中使用该对象。
  3. 在员工中将您与部门的关系标记为@Cascade

回答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 的代码在检索它的事务中至少调用了一个方法。