Shaun Scovil
Shaun Scovil

Reputation: 3987

Hibernate: How to cascade embedded objects with parent object's auto-generated ID as a foreign key

Question:

In Hibernate (JPA 2.0), how can I create embedded objects with @OneToOne relationships when I create the parent object?

Expectation:

When I create a User, I expect it to automatically create the appropriate Properties with the User's auto-generated id as the Property's object_id.

Reality:

When I try to create a User, I get the following exception:

javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.api.user.PropertyEntity.objectId

Database Tables:

I have two MySQL tables: users and properties

users
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1  | foo      | bar      |
+----+----------+----------+
| 2  | goo      | baz      |
+----+----------+----------+
| 3  | woo      | hah      |
+----+----------+----------+

properties
+----+-----------+-------------+---------+-----------+
| id | object_id | object_type | propkey | propvalue |
+----+-----------+-------------+---------+-----------+
| 1  | 1         | user        | sso     | true      |
+----+-----------+-------------+---------+-----------+
| 2  | 1         | user        | ssoid   | foobar    |
+----+-----------+-------------+---------+-----------+
| 3  | 2         | user        | sso     | false     |
+----+-----------+-------------+---------+-----------+
| 4  | 2         | user        | ssoid   | null      |
+----+-----------+-------------+---------+-----------+
| 5  | 3         | user        | sso     | false     |
+----+-----------+-------------+---------+-----------+
| 6  | 3         | user        | ssoid   | null      |
+----+-----------+-------------+---------+-----------+

Java Entities:

A User can have multiple properties, but each property has a @OneToOne relationship with that User and the relationship is unidirectional (i.e. a User creates its Properties, not the other way around).

The User entity in Java looks like this:

@Entity
@Table(name = "users")
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "username", nullable = false)
    private String userName;

    @Column(name = "password", nullable = false)
    private String password;

    @OneToOne
    @Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE })
    @JoinColumnsOrFormulas(value={
            @JoinColumnOrFormula(column=
                   @JoinColumn(name = "id", referencedColumnName = "object_id", insertable=false, updatable=false, nullable = false)
            ),
            @JoinColumnOrFormula(formula=
                    @JoinFormula(referencedColumnName="object_type", value="'user'")
            ),
            @JoinColumnOrFormula(formula=
                    @JoinFormula(referencedColumnName="propkey", value="'sso'")
            )
    })
    private PropertyEntity isSso;

    @OneToOne
    @Cascade(value = { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE })
    @JoinColumnsOrFormulas(value={
            @JoinColumnOrFormula(column=
                    @JoinColumn(name = "id", referencedColumnName = "object_id", insertable=false, updatable=false, nullable = false)
            ),
            @JoinColumnOrFormula(formula=
                    @JoinFormula(referencedColumnName="object_type", value="'user'")
            ),
            @JoinColumnOrFormula(formula=
                    @JoinFormula(referencedColumnName="propkey", value="'ssoid'")
            )
    })
    private PropertyEntity ssoId;

    // Getters & Setters
}

The Property entity looks like this:

@Entity
@Table(name = "properties")
public class PropertyEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "object_id", nullable = false)
    private Integer objectId;

    @Column(name = "object_type", nullable = false)
    private String objectType;

    @Column(name = "propkey", nullable = false)
    private String name;

    @Column(name = "propvalue", nullable = false)
    private String value;

    // Getters & Setters
}

Upvotes: 0

Views: 1198

Answers (1)

Shaun Scovil
Shaun Scovil

Reputation: 3987

Like most issues related to Hibernate annotations, I resolved this by eliminating them altogether and doing it the old fashioned way (making separate queries in my transaction for creating/updating a user and then creating/updating each user property record).

If anyone has a Hibernate solution that works, I'm happy to accept that answer.

Upvotes: -1

Related Questions