在 Hibernate 中使用 JPA 注释来描述外键仅在子 table 中的 @OneToMany 关系

Using JPA annotations with Hibernate to describe @OneToMany relationship where foreign key is only in child table

我有一个非常满意的现有数据模型:

public class Garden {
    private String name; // "Oak Grove"
    private List<Plant> plants;
}

public class Plant {
    private String name; // "Cherry Tomato"
}

我想在具有以下条件的 Hibernate 中映射它:

  1. Java 中的 Plant class 不维护对其父 Garden 的引用。这使得 Java 层的事情变得更加困难,IMO。

  2. PLANT table 应该有一个 GARDEN_ID 列,它是 GARDEN(ID) 列的外键。

我的初始设置,在添加 @OneToMany 之前:

@Entity(name = "GARDEN")
public class Garden {
    @Id
    @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "NAME", nullable = false)
    private String name; // "Oak Grove"

    // Not yet mapped
    private List<Plant> plants;
}

@Entity(name = "PLANT")   
public class Plant {
    @Id
    @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "NAME", nullable = false)
    private String name; // "Cherry Tomato"
}

如何在 List<Plant> plants; 上定义 @OneToMany 注释,以便在 Plant 中维护外键引用?

如果我只添加:

@OneToMany(cascade = { CascadeType.ALL })
@JoinColumn(name = "GARDEN_ID")
private List<Plant> plants;

然后像这样用植物保存花园失败:

Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "GARDEN_ID"; SQL statement:
insert into PLANT (NAME, ID) values (?, ?) [23502-191]

所以 Hibernate 似乎并没有尝试保留外键。有没有办法在不完全破坏我的对象模型的情况下完成此操作?

编辑: 我测试的方式是:

Garden garden = new Garden("Oak Grove");
garden.addPlant(new Plant("Cherry Tomato"));
gardenManager.save(garden);

其中save()方法看起来很Hibernate-ey:

public void save(T item) {
    try (Session session = factory.openSession()) {
        Transaction transaction = session.beginTransaction();
        try {
            session.saveOrUpdate(item);
            transaction.commit();
        } catch (Exception ex) {
            System.out.println("Error occurred saving item: " + ex.getMessage());
            ex.printStackTrace();
            transaction.rollback();
        }
    }
}

对于一对多的关系,如果工厂要保持关系,你需要使用双向来定义它。 这是我认为你应该能够实现的:

@Entity(name = "GARDEN")
public class Garden {
    @Id
    @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "NAME", nullable = false)
    private String name; // "Oak Grove"

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "garden")
    private List<Plant> plants;
}

@Entity(name = "PLANT")   
public class Plant {
    @Id
    @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "NAME", nullable = false)
    private String name; // "Cherry Tomato"

    @ManyToOne
    @JoinColumn(name = "GARDEN_ID", nullable = false)
    Garden garden;
}

这种双向方法会让实体管理器知道两者之间存在关系,因为一方面是一对多,另一方面是多对一。希望这会有所帮助。

Yogesh Sakurikar 很接近,但 bi-directional @JoinColumn 有点偏离。下面您将看到如何双向加入或 mono-riectionally

@Entity(name = "GARDEN")
public class Garden {
    @Id
    @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "NAME", nullable = false)
    private String name; // "Oak Grove"

    // use this if you don't want a bi-directional relationship
    // @OneToMany
    // @JoinColumn(name = "ID", referencedColumnName="GARDEN_ID")
    // private List<Plant> plants;

    // use this if you want it bi-directional
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "garden")
    private Set<Plant> plants;
}

@Entity(name = "PLANT")   
public class Plant {
    @Id
    @Column(name = "ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "NAME", nullable = false)
    private String name; // "Cherry Tomato"

    // use this if you don't want a bi-directional relationship
    // @Column(name="GARDEN_ID")
    // private long gardenId;

    // use this if you want a bi-directional relationship
    @ManyToOne
    @JoinColumn(name = "GARDEN_ID", referencedColumnName="ID", nullable = false)
    private Garden garden;

}

下面的代码假定 bi-directional 关系。否则你需要先了解你的 Garden.id 才能完整描述任何 child Plant

Garden garden = new Garden("Oak Grove");
Plant plant = new Plant("Cherry Tomato")
plant.setGarden(garden); //don't forget to set the parent on the child
garden.addPlant(plant);
gardenManager.save(garden);