java - Hibernate 与额外列的多对多关系

标签 java hibernate postgresql many-to-many

我在使用 JAVA 和 HIBERNATE 进行多对多关系映射时遇到问题 我有 table 购买>--------------------------------<产品,所以我们得到另一张 table 购买产品,这是 DDL

    CREATE TABLE product (
      idProduct      serial primary key,
      nameAr         varchar(50),
      nameFr         varchar(50),
      preference       varchar(50),
      qtyStart       double PRECISION,
      qtyInHand      double PRECISION,
      sellPrice      double PRECISION ,
      purchasePrice  double PRECISION,
      taxe           double PRECISION
    );


CREATE TABLE purchase (
  idPurchase      serial primary key,
  code  varchar(50) ,
  date timestamp ,
  totalHt double PRECISION,
  tva double PRECISION,
  totalTTC double PRECISION
);


CREATE TABLE purchaseProduct (
  idPurchase integer,
  idProduct integer,
  qty double PRECISION, 
  price double  PRECISION,
  primary key(idPurchase,idProduct),
  foreign key(idPurchase) references purchase(idPurchase),
  foreign key(idProduct) references product(idProduct) 
);

这是我的 hibernate.cfg.xml 配置:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">org.postgresql.Driver</property>
        <property name="connection.url">jdbc:postgresql://localhost:5432/testInventory</property>
        <property name="connection.username">postgres</property>
        <property name="connection.password">myPassword</property>


        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.PostgreSQL82Dialect</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <mapping  class="model.Product"/>
        <mapping  class="model.Purchase"/>
        <mapping  class="model.LineCommand"/>

    </session-factory>

</hibernate-configuration>

我采用这种方法来建模多对多关系:

产品映射

@Entity
@Table(name = "Product")
@Access(AccessType.PROPERTY)
public class Product {
    private LongProperty idProduct;
    private StringProperty nameAr;
    private StringProperty nameFr;
    private StringProperty preference;
    private DoubleProperty qtyStart;
    private DoubleProperty qtyInHand;
    private DoubleProperty sellPrice;
    private DoubleProperty purchasePrice;
    private DoubleProperty taxe;

    private Set<LineCommand> lineItems = new HashSet<LineCommand>(0);


    public void setIdProduct(long idProduct) {
        this.idProduct.set(idProduct);
    }


    public Product() {
        idProduct = new SimpleLongProperty();
        nameAr = new SimpleStringProperty();
        nameFr = new SimpleStringProperty();
        preference = new SimpleStringProperty();
        qtyStart = new SimpleDoubleProperty();
        qtyInHand = new SimpleDoubleProperty();
        sellPrice = new SimpleDoubleProperty();
        purchasePrice = new SimpleDoubleProperty();
        taxe = new SimpleDoubleProperty();
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq_gen")
    @SequenceGenerator(name = "product_seq_gen", sequenceName = "product_idproduct_seq", initialValue = 1, allocationSize = 1)
    @Column(name = "idproduct", unique = true, nullable = false)
    public Long getIdProduct() {
        return idProduct.get();
    }

    public LongProperty idProductProperty() {
        return idProduct;
    }

    public void setIdProduct(Long idProduct) {
        this.idProduct.set(idProduct);
    }

    @Column(name = "nameAr")
    public String getNameAr() {
        return nameAr.get();
    }

    public StringProperty nameArProperty() {
        return nameAr;
    }

    public void setNameAr(String nameAr) {
        this.nameAr.set(nameAr);
    }

    @Column(name = "nameFr")
    public String getNameFr() {
        return nameFr.get();
    }

    public StringProperty nameFrProperty() {
        return nameFr;
    }

    public void setNameFr(String nameFr) {
        this.nameFr.set(nameFr);
    }

    @Column(name = "preference")
    public String getPreference() {
        return preference.get();
    }

    public StringProperty preferenceProperty() {
        return preference;
    }

    public void setPreference(String preference) {
        this.preference.set(preference);
    }

    @Column(name = "qtyStart")
    public double getQtyStart() {
        return qtyStart.get();
    }

    public DoubleProperty qtyStartProperty() {
        return qtyStart;
    }

    public void setQtyStart(double qtyStart) {
        this.qtyStart.set(qtyStart);
    }

    @Column(name = "qtyInHand")
    public double getQtyInHand() {
        return qtyInHand.get();
    }

    public DoubleProperty qtyInHandProperty() {
        return qtyInHand;
    }

    public void setQtyInHand(double qtyInHand) {
        this.qtyInHand.set(qtyInHand);
    }

    @Column(name = "sellPrice")
    public double getSellPrice() {
        return sellPrice.get();
    }

    public DoubleProperty sellPriceProperty() {
        return sellPrice;
    }

    public void setSellPrice(double sellPrice) {
        this.sellPrice.set(sellPrice);
    }

    @Column(name = "purchasePrice")
    public double getPurchasePrice() {
        return purchasePrice.get();
    }

    public DoubleProperty purchasePriceProperty() {
        return purchasePrice;
    }

    public void setPurchasePrice(double purchasePrice) {
        this.purchasePrice.set(purchasePrice);
    }

    @Column(name = "taxe")
    public double getTaxe() {
        return taxe.get();
    }

    public DoubleProperty taxeProperty() {
        return taxe;
    }

    public void setTaxe(double taxe) {
        this.taxe.set(taxe);
    }

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.product", cascade=CascadeType.ALL)
    public Set<LineCommand> getLineItems() {
        return lineItems;
    }

    public void setLineItems(Set<LineCommand> lineItems) {
        this.lineItems = lineItems;
    }

}

购买:

@Entity
@Table(name = "purchase")
@Access(AccessType.PROPERTY)
public class Purchase {
    private LongProperty idPurchase;
    private StringProperty codePurchase;
    private ObjectProperty<Timestamp> datePurchase;
    private DoubleProperty totalHt;
    private DoubleProperty tva;
    private DoubleProperty totalTTC;

    private Set<LineCommand> lineItems = new HashSet<LineCommand>(0);

    public Purchase() {
        this.idPurchase = new SimpleLongProperty();
        this.codePurchase = new SimpleStringProperty();
        this.datePurchase = new SimpleObjectProperty<>();
        this.totalHt = new SimpleDoubleProperty();
        this.tva = new SimpleDoubleProperty();
        this.totalTTC = new SimpleDoubleProperty();
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "purchase_seq_gen")
    @SequenceGenerator(name = "purchase_seq_gen", sequenceName = "purchase_idpurchase_seq", initialValue = 1, allocationSize = 1)
    @Column(name = "idpurchase", unique = true, nullable = false)
    public long getIdPurchase() {
        return idPurchase.get();
    }

    public LongProperty idPurchaseProperty() {
        return idPurchase;
    }

    public void setIdPurchase(long idPurchase) {
        this.idPurchase.set(idPurchase);
    }

    @Column(name = "code")
    public String getCodePurchase() {
        return codePurchase.get();
    }

    public StringProperty codePurchaseProperty() {
        return codePurchase;
    }

    public void setCodePurchase(String codePurchase) {
        this.codePurchase.set(codePurchase);
    }

    @Column(name = "date")
    public Timestamp getDatePurchase() {
        return datePurchase.get();
    }

    public ObjectProperty<Timestamp> datePurchaseProperty() {
        return datePurchase;
    }

    public void setDatePurchase(Timestamp datePurchase) {
        this.datePurchase.set(datePurchase);
    }

    @Column(name = "totalHt")
    public double getTotalHt() {
        return totalHt.get();
    }

    public DoubleProperty totalHtProperty() {
        return totalHt;
    }

    public void setTotalHt(double totalHt) {
        this.totalHt.set(totalHt);
    }

    @Column(name = "tva")
    public double getTva() {
        return tva.get();
    }

    public DoubleProperty tvaProperty() {
        return tva;
    }

    public void setTva(double tva) {
        this.tva.set(tva);
    }

    @Column(name = "totalTTC")
    public double getTotalTTC() {
        return totalTTC.get();
    }

    public DoubleProperty totalTTCProperty() {
        return totalTTC;
    }

    public void setTotalTTC(double totalTTC) {
        this.totalTTC.set(totalTTC);
    }


    @OneToMany(mappedBy = "pk.purchase",
            cascade = CascadeType.ALL)
    public Set<LineCommand> getLineItems() {
        return this.lineItems;
    }

    public void setLineItems(Set<LineCommand> lineItems) {
        this.lineItems = lineItems;
    }

}

购买产品:

@Entity
@Table(name = "purchaseProduct")
@Access(AccessType.PROPERTY)
@AssociationOverrides({
        @AssociationOverride(name = "pk.product",
                joinColumns = @JoinColumn(name = "idProduct")),
        @AssociationOverride(name = "pk.purchase",
                joinColumns = @JoinColumn(name = "idPurchase"))})
public class LineCommand {

    // private LongProperty idProduct;
    //  private LongProperty idCommand;
    private DoubleProperty qty;
    private DoubleProperty sellPrice;
    private DoubleProperty subTotal;

    private LineCommandId compositePrimaryKey = new LineCommandId();

    @EmbeddedId
    public LineCommandId getCompositePrimaryKey() {
        return compositePrimaryKey;
    }

    public void setCompositePrimaryKey(LineCommandId compositePrimaryKey) {
        this.compositePrimaryKey = compositePrimaryKey;
    }


    private Product product;
    private Purchase purchase;

    public LineCommand() {
        // this.idProduct = new SimpleLongProperty();
        // this.idCommand = new SimpleLongProperty();
        this.qty = new SimpleDoubleProperty();
        this.sellPrice = new SimpleDoubleProperty();
        this.subTotal = new SimpleDoubleProperty();
        // Bind subtotal to qty * sellPrice
        this.subTotalProperty().bind(Bindings.multiply(this.qtyProperty(), this.sellPriceProperty()));
    }

    public LineCommand(double qty, double sellPrice) {
        //  this.idProduct.set(idProduct);
        // this.idCommand.set(idCommand);
        this.qty.set(qty);
        this.sellPrice.set(sellPrice);

        // Bind subtotal to qty * sellPrice
        this.subTotalProperty().bind(Bindings.multiply(this.qtyProperty(), this.sellPriceProperty()));
    }

    @Column(name = "qty")
    public double getQty() {
        return qty.get();
    }

    public DoubleProperty qtyProperty() {
        return qty;
    }

    public void setQty(double qty) {
        this.qty.set(qty);
    }

    @Column(name = "price")
    public double getSellPrice() {
        return sellPrice.get();
    }

    public DoubleProperty sellPriceProperty() {
        return sellPrice;
    }

    public void setSellPrice(double sellPrice) {
        this.sellPrice.set(sellPrice);
    }


    public double getSubTotal() {
        return subTotal.get();
    }

    public DoubleProperty subTotalProperty() {
        return subTotal;
    }

    @Transient
    public Product getProduct() {
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

    @Transient
    public Purchase getPurchase() {
        return purchase;
    }

    public void setPurchase(Purchase purchase) {
        this.purchase = purchase;
    }
}

购买产品复合键

@Embeddable
public class LineCommandId implements Serializable{
    private Product product ;
    private Purchase purchase ;

    @ManyToOne(cascade = CascadeType.ALL)
    public Product getProduct() {
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

    @ManyToOne(cascade = CascadeType.ALL)
    public Purchase getPurchase() {
        return purchase;
    }

    public void setPurchase(Purchase purchase) {
        this.purchase = purchase;
    }
}

当我执行上述代码时,出现此错误:

org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: model.LineCommand.pk.product in model.Product.lineItems
    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:769)
    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:719)
    at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54)
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1655)
    at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1623)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:278)
    at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
    at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:418)
    at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:87)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:692)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:724)
    at util.DatabaseUtil.buildSessionFactory(DatabaseUtil.java:17)
    at util.DatabaseUtil.<clinit>(DatabaseUtil.java:11)
    at dao.DAO.<init>(DAO.java:13)
    at dao.ProductDAO.<init>(ProductDAO.java:14)
    at controller.product.productController.parentTableProperties(productController.java:79)
    at controller.product.productController.tableProperties(productController.java:74)
    at controller.product.productController.initialize(productController.java:66)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
    at mains.start(mains.java:21)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$52/479874812.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$48/704060124.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$50/1324097194.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$49/1608446104.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$145(WinApplication.java:101)
    at com.sun.glass.ui.win.WinApplication$$Lambda$38/1378653614.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(LauncherImpl.java:182)
    at com.sun.javafx.application.LauncherImpl$$Lambda$2/932172204.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ExceptionInInitializerError
    at dao.DAO.<init>(DAO.java:13)
    at dao.ProductDAO.<init>(ProductDAO.java:14)
    at controller.product.productController.parentTableProperties(productController.java:79)
    at controller.product.productController.tableProperties(productController.java:74)
    at controller.product.productController.initialize(productController.java:66)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
    at mains.start(mains.java:21)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
    at com.sun.javafx.application.LauncherImpl$$Lambda$52/479874812.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl$$Lambda$48/704060124.run(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
    at com.sun.javafx.application.PlatformImpl$$Lambda$50/1324097194.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
    at com.sun.javafx.application.PlatformImpl$$Lambda$49/1608446104.run(Unknown Source)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$145(WinApplication.java:101)
    at com.sun.glass.ui.win.WinApplication$$Lambda$38/1378653614.run(Unknown Source)
    ... 1 more
Caused by: java.lang.RuntimeException: There was an error building the factor
    at util.DatabaseUtil.buildSessionFactory(DatabaseUtil.java:20)
    at util.DatabaseUtil.<clinit>(DatabaseUtil.java:11)
    ... 23 more
DEBUG - Connection pool now considered primed; min-size will be maintained

最佳答案

参数mappedBy应该是所属类的字段或属性,而不是关系名称,因此您需要 mappedBy = "purchase"mappedBy = "product" 。事实上,使用复合键,它应该是 @IdClass 中的属性名称。这就是映射关系的那部分。

如果您要遵循此路径,那么您还应该删除 @EmbeddedId注释并将其替换为 @IdClass - 它更加干净、简单并且符合 JPA 标准。

您可以删除对 compositePrimaryKey 的任何引用并替换@Transient方法上的注释 getProduct()getPurchase() LineCommand的类:

@Id
@ManyToOne
@JoinColumn(name = "idproduct", updatable = false, insertable = false, referencedColumnName = "idproduct")

只需修改这些方法即可使用 Product 的实际实例和Purchase ,您需要将其添加到 LineCommand类而不是 compositePrimaryKey .

当然,你的@Embeddable LineCommandId那么类应该没有注释,因为它将被用作 @IdClass (请参阅下文了解为什么此处使用 Integer 而不是 Long):

@SuppressWarnings("serial")
public class LineCommandId implements Serializable {
    private Integer product;
    private Integer purchase;
    public Integer getProduct() {
        return product;
    }
    public void setProduct(Integer idproduct) {
        this.product = idproduct;
    }
    public Integer getPurchase() {
        return purchase;
    }
    public void setPurchase(Integer idpurchase) {
        this.purchase = idpurchase;
    }
    public boolean equals(Object foo) {
        if (foo == null || foo.getClass() != this.getClass())
            return false;
        LineCommandId src = (LineCommandId)foo;
        if ((this.getProduct() == src.getProduct()) &&
            (this.getPurchase() == src.getPurchase()))
            return true;
        return false;
    }
    public int hashCode() {
        // implement
        return someHashCode;
    }
}

不过,您需要确保您的基本实体(例如 Product )使用 LineCommandId 中的字段名称。在其 mappedBy @OneToMany 的属性注释。

我注意到的另一件事是你计算 subTotal动态属性。由于它也不是数据库架构的一部分,因此 that 是您需要使用 @Transient 进行注释的属性。 ,否则您将收到有关 Hibernate 的异常 unable to find setter .

NullPointerException最有可能是由于您的包装对象返回 null来自get()方法,例如 idProduct属性,当包装对象未初始化时:

public Long getIdProduct() {
    return idProduct.get();
}

更可能的 NPE 是您的某些 getter 方法使用自动装箱从非原始类型转换为原始类型:

public long getIdPurchase() {
    return idPurchase.get();
}

如果idPurchase.get()返回null ,自动装箱的隐式 null.longValue()将导致异常。

一旦解决了启动持久性单元的最初问题,您还会注意到 LongSERIAL 根本不兼容,您在表中使用的:SERIAL映射到INTEGERLong映射到BIGINT 。您可能想更改它以匹配。这也是为什么上面的例子LineCommandId正在使用 Integer s。

关于java - Hibernate 与额外列的多对多关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35105493/

相关文章:

java - 从另一个 Maven 项目调用测试类

Java Map util方法深度检查containKey?

java - JPA:向多对多添加新条目需要很长时间

ruby-on-rails - 让用户订阅关键字的好方法是什么

python - 如何使用 Dask 从谷歌云存储中读取多个大型 CSV 文件的 block ,而不会使内存一次性重载

Java和MySQL查询日期

java - 如何使用 SpriteBatch 绘制方法

java - 如何通过基于 Java 的配置在 Hibernate 中的查询上使用分页

java - list() 返回什么类型的列表?

Django-Postgres 完整性错误 : Duplicate key --- how to fix?