1

I have 2 Entities that I am trying to set up using a @OneToOne relationship.

I am using Oracle 11g as the database and User Table has a generated ID via a sequence. This Id from the user table is the Primary Key and Foreign key in the UserProfile table.

So basically a User is mapped to 1 and only 1 User Profile and a User Profile maps back to only 1 User.

I have seeded data in my database and I can read User Object with a User Profile without problem. But when I try to insert a new User Object with a New UserProfile object in a transaction it fails with a Foreign Key Violation in my UserProfile table.

It seems to be a common problem out there with a @OneToOne with Shared primary key, but I can't see to find a answer that works. Can someone point me in the right direction?

I can see that the Id in USerProfile gets set correctly, just on the commit to the database it throws the violation. It almost seems like the UserProfile is being committed to the database before User. Is there a way to persist the User first without have to persist these objects separately?

Here are my entities:

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;
}
}

Here is my DAO and 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"/>

And here is my output:

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
4

1 回答 1

1

你的映射是错误的。在双向关联中,一侧是关联的所有者,并说明关联是如何映射的。另一边是反面,使用mappedBy属性只是说“看另一边的映射”。

请参阅文档以获取示例:

@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;
}
于 2013-01-13T16:38:21.127 回答