speshak
speshak

Reputation: 2477

Hibernate annotation many-to-one mapping generating odd schema

I have a fairly simple Many to One relationship between two classes:

@Entity
public class Schedule
 implements java.io.Serializable {

    private String scheduleName;
    private HashSet<Step> steps;

    @OneToMany(mappedBy="schedule", cascade=CascadeType.ALL, 
        fetch=FetchType.EAGER)
    public HashSet<Step> getSteps() {
       return steps;
    }
}

@Entity
public class Step implements java.io.Serializable {
   private Long id; 
   private String duration;
   private String stepType;
   private Schedule schedule;

   @ManyToOne(fetch=FetchType.LAZY)
   public Schedule getSchedule() {
     return schedule;
   }

   @Id
   @GeneratedValue
   public Long getId() {
       return id;
   }

}

Hibernate generates the following tables (in Postgres)

              Table "public.schedule"
    Column    |          Type          | Modifiers 
--------------+------------------------+-----------
 uuid         | character varying(255) | not null
 version      | integer                | 
 schedulename | character varying(255) | 
 steps        | bytea                  | 


                Table "public.step"
    Column     |          Type          | Modifiers 
---------------+------------------------+-----------
 id            | bigint                 | not null
 duration      | character varying(255) | 
 steptype      | character varying(255) | 
 temperature   | numeric(19,2)          | 
 schedule_uuid | character varying(255) | 

The step table is what I expect, but I don't understand why the steps(bytea) column is there. Am I doing something wrong in my mapping or do I just not understand how hibernate works?

Upvotes: 3

Views: 13220

Answers (1)

Pascal Thivent
Pascal Thivent

Reputation: 570575

I suspect the problem is that you're using a concrete HashSet instead of the Set interface. Try this (assuming it has an Id somewhere):

@Entity
public class Schedule implements java.io.Serializable {

    private String scheduleName;
    private Set<Step> steps = new HashSet<Step>();

    @OneToMany(mappedBy="schedule", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    public Set<Step> getSteps() {
       return steps;
    }

    // other properties, getters, setters
}

Also note how I initialized the steps property. Let me quote the documentation about this:

6.1. Persistent collections

...

Notice how the instance variable was initialized with an instance of HashSet. This is the best way to initialize collection valued properties of newly instantiated (non-persistent) instances. When you make the instance persistent, by calling persist() for example, Hibernate will actually replace the HashSet with an instance of Hibernate's own implementation of Set.

And make sure that:

  • both entities have an @Id property (the part you're showing is not enough to confirm that).
  • Step is implementing equals/hashCode correctly (see the references below).

References


Update: Can't reproduce (I don't have PostgreSQL installed by I don't think it is that relevant). I used the following entities:

@Entity
public class Step implements java.io.Serializable {
    private Long id;
    private String duration;
    private String stepType;
    private Schedule schedule;

    @ManyToOne(fetch = FetchType.LAZY)
    public Schedule getSchedule() { return schedule; }

    @Id @GeneratedValue
    public Long getId() { return id; }

    // getters, setters, equals, hashCode
}

And:

@Entity
public class Schedule implements java.io.Serializable {
    private Long id;
    private String scheduleName;
    private Set<Step> steps = new HashSet<Step>();

    @OneToMany(mappedBy = "schedule", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    public Set<Step> getSteps() { return steps; }

    @Id @GeneratedValue
    public Long getId() { return id; }

    // getters, setters
}

Here is the generated DDL:

create table Schedule (
    id bigint generated by default as identity (start with 1),
    scheduleName varchar(255),
    primary key (id)
)

create table Step (
    id bigint generated by default as identity (start with 1),
    duration varchar(255),
    stepType varchar(255),
    schedule_id bigint,
    primary key (id)
)

alter table Step 
    add constraint FK277AEC7B775928 
    foreign key (schedule_id) 
    references Schedule

I don't even understand how you could use a HashSet in your OneToMany, Hibernate complained (as expected to be honest) when I tried:

Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: com.stackoverflow.q4083744.Schedule.steps

Upvotes: 6

Related Questions