Search code examples
javaspringhibernatejpaspring-data-jpa

Extra columns in table used in @JoinTable annotation


I'm creating a REST API using Spring Boot and Hibernate.

I have two tables, let's call them table Foo and table Bar. They have an 1-N association that is mapped through a third table, let's call it FooBarAssociation. I created this association between them using the @JoinTable annotation.

Here is a minimum example of what I have now.

public class Foo {
    @Id
    @Column(name = "foo_id");
    Long fooId;
}

public class Bar{

    @Id
    @Column(name = "bar_id");
    Long barId;

    @ManyToOne
    @JoinTable(name = "Foo ", joinColumns = @JoinColumn(name = "bar_id"), inverseJoinColumns = @JoinColumn(name = "foo_id"))
    Foo foo;

}

The thing is, the table FooBarAssociation also has a third column that should contain the date that the relation was created. So there are three columns: foo_id, bar_id and date. When hibernate tries to insert a new entry to FooBarAssociation it generates as exception, as the column date does not allow null values. Is there a way I can tell hibernate how to fill this extra column?

insert into FooBarAssociation (foo_id, bar_id) values (?, ?)

Cannot insert the value NULL into column 'date', table 'FooBarAssociation'; column does not allow nulls. INSERT fails.

Solution

  • Well since this relation is manadatory the only way I see to modify middle layer, is to obtain control over it explicitly. This will help.

    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    public class Bar {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        Long id;
    
        @ManyToOne(cascade = CascadeType.ALL)
        @JoinColumn(name = "bar_id")
        FooBarRelation relation;
    
        public void setFoo(Foo foo) {
            relation = new FooBarRelation(this, foo);
        }
    }
    
    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    @EntityListeners(AuditingEntityListener.class)
    public class FooBarRelation {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        Long id;
    
        @OneToOne(cascade = CascadeType.ALL)
        @JoinColumn(name = "foo_id")
        Foo foo;
    
        @OneToOne(cascade = CascadeType.ALL)
        @JoinColumn(name = "bar_id")
        Bar bar;
    
        @LastModifiedDate
        LocalDateTime dateTime;
    
        public FooBarRelation(Bar bar, Foo foo) {
            this.bar = bar;
            this.foo = foo;
        }
    }
    
    
    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    public class Foo {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        Long id;
    }
    

    Test:

      @Autowired
        public BarRepo barRepo;
    
        @Test
        public void testBar(){
            Bar bar = new Bar();
            bar.setFoo(new Foo());
            barRepo.save(bar);
            List<Bar> all = barRepo.findAll();
            FooBarRelation relation = all.get(0).getRelation();
            assertNotNull(relation.getBar());
            assertNotNull(relation.getFoo());
            assertNotNull(relation.getDateTime());
        }
    

    Cascade types and other minor details on you ;)