What is the relation between hibernate @version and 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.
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.
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);
}
}
You need to first save the Department
before saving the Employee
.
service.save(dept);
service.save(emp);
UPDATE in 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: