我正在尝试使用@OneToOne关系设置2个实体。
我使用Oracle 11g作为数据库,用户表通过序列生成ID。用户表中的此Id是UserProfile表中的主键和外键。
所以基本上用户被映射到1,只有1个用户档案和用户档案只能映射回1个用户。
我在我的数据库中播种了数据,我可以使用用户配置文件读取用户对象而没有任何问题。但是当我尝试在事务中插入一个带有New UserProfile对象的新用户对象时,它在UserProfile表中失败并出现外键冲突。
使用共享主键的@OneToOne似乎是一个常见的问题,但我看不到找到有效的答案。有人能指出我正确的方向吗?
我可以看到USerProfile中的Id设置正确,只是在提交数据库时它会引发违规。似乎UserProfile在User之前被提交到数据库。有没有办法先保留用户而不必单独保留这些对象?
以下是我的实体:
package com.company.ca.domain;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name="USERS")
@SequenceGenerator(name="USERS_SEQ", sequenceName="USERS_SEQ", allocationSize=1)
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="USERS_SEQ")
private Long id;
@Column(name="USERNAME", unique=true)
private String email;
private String password;
@Column(name="CREATED_DATE", insertable=false, updatable=false)
private Date createdDate;
@Column(name="CREATED_BY")
private String createdBy;
@Column(name="LAST_UPDATED_DATE")
private Date lastUpdatedDate;
@Column(name="LAST_UPDATED_BY")
private String lastUpdatedBy;
private int enabled;
@OneToOne(cascade = CascadeType.ALL, mappedBy="user")
@PrimaryKeyJoinColumn(name="ID", referencedColumnName="USER_ID")
private UsersProfile usersProfile;
public Users() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getEnabled() {
return enabled;
}
public void setEnabled(int enabled) {
this.enabled = enabled;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Date getLastUpdatedDate() {
return lastUpdatedDate;
}
public void setLastUpdatedDate(Date lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
}
public String getLastUpdatedBy() {
return lastUpdatedBy;
}
public void setLastUpdatedBy(String lastUpdatedBy) {
this.lastUpdatedBy = lastUpdatedBy;
}
public UsersProfile getUsersProfile() {
return usersProfile;
}
public void setUsersProfile(UsersProfile usersProfile) {
this.usersProfile = usersProfile;
}
}
package com.company.ca.domain;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
@Entity
@Table(name="USERS_PROFILE")
@org.hibernate.annotations.GenericGenerator(name="user-primarykey", strategy="foreign",
parameters={@org.hibernate.annotations.Parameter(name="property", value="user")
})
public class UsersProfile implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "user-primarykey")
@Column(name = "USER_ID")
private Long userId;
@OneToOne
@PrimaryKeyJoinColumn(name="USER_ID", referencedColumnName="ID")
private Users user;
@Column(name="FIRST_NAME")
private String firstName;
@Column(name="LAST_NAME")
private String lastName;
@Column(name="PHONE_NUMBER")
private String phoneNumber;
@Column(name="CREATED_DATE", insertable=false, updatable=false)
private Date createdDate;
@Column(name="CREATED_BY")
private String createdBy;
@Column(name="LAST_UPDATED_DATE")
private Date lastUpdatedDate;
@Column(name="LAST_UPDATED_BY")
private String lastUpdatedBy;
public UsersProfile() {
super();
}
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;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Date getLastUpdatedDate() {
return lastUpdatedDate;
}
public void setLastUpdatedDate(Date lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
}
public String getLastUpdatedBy() {
return lastUpdatedBy;
}
public void setLastUpdatedBy(String lastUpdatedBy) {
this.lastUpdatedBy = lastUpdatedBy;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
}
这是我的DAO和JUnit:
package com.company.ca.persistence;
import org.springframework.data.repository.CrudRepository;
import com.company.ca.domain.Users;
public interface UserDAO extends CrudRepository<Users, Long> {
}
package com.company.ca.persistence.test;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import com.company.ca.domain.Users;
import com.company.ca.domain.UsersProfile;
import com.company.ca.persistence.UserDAO;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
(
locations = {
"classpath*:/spring-persistence.xml"
}
)
@TransactionConfiguration(defaultRollback=false)
@Service
public class UserDAOImplTest extends AbstractTransactionalJUnit4SpringContextTests {
protected final Log logger = LogFactory.getLog(getClass());
@Autowired
UserDAO userDAO;
@Test
public void testFindUser() {
Users testUser = userDAO.findOne(new Long(2));
Assert.assertNotNull("User Object should not be null", testUser);
Assert.assertNotNull("User Profile should not be null", testUser.getUsersProfile());
}
@Test
//@Transactional(propagation=Propagation.MANDATORY)
public void testCreateUser() {
String email = "test.user2@testCompany.com";
String firstName = "Test 2";
String lastName = "User";
String phoneNumber = "1234567890";
String createdBy = "IT_SYSTEM@system.com";
String password = "password";
// now lets create the user
Users user = new Users();
user.setCreatedBy(createdBy);
user.setPassword(password);
user.setEmail(email);
user.setEnabled(0);
UsersProfile userProfile = new UsersProfile();
userProfile.setCreatedBy(createdBy);
userProfile.setFirstName(firstName);
userProfile.setLastName(lastName);
userProfile.setPhoneNumber(phoneNumber);
userProfile.setUser(user);
user.setUsersProfile(userProfile);
userDAO.save(user);
Assert.assertNotNull("UserId was not returned...", user.getId());
Assert.assertEquals("Expected email not set correctly", email,
user.getEmail());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<context:annotation-config />
<context:property-placeholder location="classpath*:/persistence.properties" />
<jpa:repositories base-package="com.company.ca.persistence"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.company.ca.domain" />
<property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.max_fetch_depth">5</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
这是我的输出:
10:28:13,400 DEBUG SQL:104 - select USERS_SEQ.nextval from dual
Hibernate: select USERS_SEQ.nextval from dual
10:28:13,500 DEBUG SQL:104 - insert into USERS (CREATED_BY, USERNAME, enabled, LAST_UPDATED_BY, LAST_UPDATED_DATE, password, id) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into USERS (CREATED_BY, USERNAME, enabled, LAST_UPDATED_BY, LAST_UPDATED_DATE, password, id) values (?, ?, ?, ?, ?, ?, ?)
10:28:13,501 TRACE BasicBinder:83 - binding parameter [1] as [VARCHAR] - VRS_SYSTEM@audatex.com
10:28:13,501 TRACE BasicBinder:83 - binding parameter [2] as [VARCHAR] - colin.moore3@audatex.com
10:28:13,502 TRACE BasicBinder:83 - binding parameter [3] as [INTEGER] - 0
10:28:13,502 TRACE BasicBinder:71 - binding parameter [4] as [VARCHAR] - <null>
10:28:13,504 TRACE BasicBinder:71 - binding parameter [5] as [TIMESTAMP] - <null>
10:28:13,505 TRACE BasicBinder:83 - binding parameter [6] as [VARCHAR] - test
10:28:13,505 TRACE BasicBinder:83 - binding parameter [7] as [BIGINT] - 117
10:28:13,577 DEBUG SQL:104 - insert into USERS_PROFILE (CREATED_BY, FIRST_NAME, LAST_NAME, LAST_UPDATED_BY, LAST_UPDATED_DATE, PHONE_NUMBER, USER_ID) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into USERS_PROFILE (CREATED_BY, FIRST_NAME, LAST_NAME, LAST_UPDATED_BY, LAST_UPDATED_DATE, PHONE_NUMBER, USER_ID) values (?, ?, ?, ?, ?, ?, ?)
10:28:13,579 TRACE BasicBinder:83 - binding parameter [1] as [VARCHAR] - VRS_SYSTEM@audatex.com
10:28:13,579 TRACE BasicBinder:83 - binding parameter [2] as [VARCHAR] - Colin
10:28:13,579 TRACE BasicBinder:83 - binding parameter [3] as [VARCHAR] - Moore
10:28:13,580 TRACE BasicBinder:71 - binding parameter [4] as [VARCHAR] - <null>
10:28:13,580 TRACE BasicBinder:71 - binding parameter [5] as [TIMESTAMP] - <null>
10:28:13,580 TRACE BasicBinder:83 - binding parameter [6] as [VARCHAR] - 4164983787
10:28:13,581 TRACE BasicBinder:83 - binding parameter [7] as [BIGINT] - 117
10:28:13,715 WARN SqlExceptionHelper:143 - SQL Error: 2291, SQLState: 23000
10:28:13,716 ERROR SqlExceptionHelper:144 - ORA-02291: integrity constraint (SYSTEM.FK_USER_ID) violated - parent key not found
10:28:13,716 WARN SqlExceptionHelper:143 - SQL Error: 2291, SQLState: 23000
10:28:13,716 ERROR SqlExceptionHelper:144 - ORA-02291: integrity constraint (SYSTEM.FK_USER_ID) violated - parent key not found
10:28:13,719 ERROR BatchingBatch:119 - HHH000315: Exception executing batch [ORA-02291: integrity constraint (SYSTEM.FK_USER_ID) violated - parent key not found
答案 0 :(得分:1)
您的映射是错误的。在双向联合中,一方是关联的所有者,并告知关联如何映射。另一边是反面,只是使用mappedBy
属性说“查看另一面的映射”。
有关示例,请参阅the documentation:
@Entity
class UserProfile {
@Id Integer id;
@MapsId @OneToOne
@JoinColumn(name = "user_id")
User user;
}
@Entity
class Person {
@Id @GeneratedValue
Integer id;
@OneToOne(mappedBy = "user")
private UserProfile profile;
}