5

I'm using Hibernate 5 and Spring 3.2.4. I'm deigning a User entity in which I want to include a reference to the user that has created the entity - so a self reference. The self reference itself isn't too problematic, but I want to specify the field as non null. Is this possible? How do I create the first entry in the DB if the field is non null as there referenced entity does not already exist?

Ex:

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long User.id;

    @NotNull
    private String username;

    private String password;

    @NotNull
    private User user;


    // getters and setters omitted for brevity
}

If I try:

User u = new User();
u.setUsername("username");
u.setCreatedBy(u);

and try to persist u, I get the following error message:

Caused by: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.domain.User.createdBy -> com.domain.User

I understand that Hibernate is complaining that it cannot reference a transient instance (ie: u), but I cannot persist u unless I have an non-null User that I can reference. But in an empty DB, there is no such entry.

Is this kind of configuration impossible to do? Or is there a way around this?

Bacteria
  • 8,406
  • 10
  • 50
  • 67
Eric B.
  • 23,425
  • 50
  • 169
  • 316
  • Have you tried specifying mapping with @ManyToOne ? Also I don't see identifier field, have you omitted it like setters? – elusive-code Nov 29 '13 at 04:22
  • @elusive-code - I updated the entity without the `@Roo` annotations to show the additional files. Yes - I've left the getters/setters omitted for brevity. The difference between @ManyToOne and @NotNull, to my understanding, is that the @ManyToOne will be used in the DDL generation and when persisting the bean, however the @NotNull can be used for bean validation even when not persisting the bean. – Eric B. Nov 29 '13 at 16:27

3 Answers3

4

I don't understand this Roo annotations and I don't use Hibernate-specific annotations, only JPA. I don't have any issues with self references. But I have some hints fo you:

  1. As mentioned before, use @ManyToOne annotation.
  2. AFAIK, @NotNull annotation (or nullable field in @Column) does not affect mapping, only DDL generation. I don't use DDL generation from domain model, do I never specify this. Instead I use optional field of @ManyToOne.
  3. What identifier generation strategy you use? If autoincrement, self-references are impossible with NOT NULL constraint. So either use sequence-based identifier generator or remove constraint. I would use first.
  4. As I mentioned, set optional field of @ManyToOne to false, when you have NOT NULL constraint. Otherwise Hibernate attempts to make two queries: insert with createdBy_id set to NULL and then update createdBy_id. And the first query fails with NOT NULL contraint enabled.
Alexey Andreev
  • 1,980
  • 2
  • 17
  • 29
  • I've edited the entity to provide example without the `@Roo` annotations. Yes - I am using autoincrement for this table, had not considered using some other GUID strategy. I will try a sequence-based identifier. THe difference between the Hibernate annotation and `@NotNull`, however, to my understanding is that the former will ensure DB schema settings, whereas the latter will work on bean validation even if not persisting to the DB. – Eric B. Nov 29 '13 at 16:14
  • I changed by Auto generation to a Table generation (MySQL does not support sequences). Now seems to work as desired. Thanks! – Eric B. Nov 29 '13 at 17:26
2

I found a solution for this. You must use a Sequence generator for your ID (instead of the default auto generated IDs). Then it works.

@Entity
public class UserModel {
  /**
   * We must use a sequence for generating IDs,
   * because of the self reference .
   * https://vladmihalcea.com/2014/07/15/from-jpa-to-hibernates-legacy-and-enhanced-identifier-generators/
   */
  @Id
  @GeneratedValue(generator = "sequence", strategy=GenerationType.SEQUENCE)
  @SequenceGenerator(name = "sequence", allocationSize = 10)
  private Long id;

  /** reference to a parent user, e.g. the manager */
  @ManyToOne(optional = false)
  @NotNull
  UserModel parentUser;

  [...]

The reason is the following: When Hibernate tries to insert a new User it also tries to validate the reference to the parentUser. But that will fail, for the first user we want to insert, or will also fail when a user references himself.

But when IDs are generated with a sequence, then the new/next ID is already known at the time of insert.

Robert
  • 1,579
  • 1
  • 21
  • 36
0

The field createdBy of u can not be null because of the annotation @NOTNULL. But u referes to itself and its not persist before saving it. You can set another persisted User for u, not itself.

  • your assumption is right with auto-increment primary key generation. But when using sequence-based PK generator, ID is already known before insertion. So it is really possibly to perform a valid `INSERT` query. – Alexey Andreev Nov 29 '13 at 06:08
  • 1
    Like I said in my original post, I knew that is what was causing the problem. However the question remains - how do you create the first row in the table? There is no other user to to assign it to. – Eric B. Nov 29 '13 at 16:15