[英]Hibernate many to many relationship with extras columns
I have an issue with many to many relationship mapping with JAVA and HIBERNATE I have table purchase >------------------< product, so we get another table purchaseProduct, here is the DDL 我在使用JAVA和HIBERNATE建立多对多关系映射时遇到问题,我购买了表格-> ------------------产品,因此我们得到了另一个表格PurchaseProduct,这是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)
);
Here is my hibernate.cfg.xml configuraiton : 这是我的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>
I take this approche to model many to many relationship: 我采用这种方法来模拟多对多关系:
Product mapping 产品映射
@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;
}
}
Purchase : 采购:
@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;
}
}
purchaseProduct: 购买产品:
@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;
}
}
purchaseProduct composite key 购买产品组合键
@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;
}
}
When i tray to execute the above code i get this error : 当我执行上述代码时,出现此错误:
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
The parameter to mappedBy
should be the field or property of the owning class, not the relation name, so you need mappedBy = "purchase"
and mappedBy = "product"
. mappedBy
的参数应该是所属类的字段或属性,而不是关系名称,因此您需要mappedBy = "purchase"
和mappedBy = "product"
。 Infact, with composite keys, it should be the name of the property in @IdClass
that is mapping that part of the relationship. 实际上,使用组合键,它应该是@IdClass
中映射关系的那部分的属性的名称。
If you are going to follow this path, then you should also drop the @EmbeddedId
annotation and replace it with @IdClass
- it is much cleaner, easier and JPA-compliant. 如果要遵循此路径,那么还应该删除@EmbeddedId
批注,并用@IdClass
替换它-它更干净,更容易且符合JPA。
You can remove any reference to compositePrimaryKey
and replace @Transient
annotations on the methods getProduct()
and getPurchase()
of the LineCommand
class with: 您可以删除对compositePrimaryKey
任何引用,并将LineCommand
类的方法getProduct()
和getPurchase()
上的@Transient
注释替换为:
@Id
@ManyToOne
@JoinColumn(name = "idproduct", updatable = false, insertable = false, referencedColumnName = "idproduct")
Just modify those methods to work with actual instances of Product
and Purchase
, which you need to add to the LineCommand
class instead of compositePrimaryKey
. 只需修改这些方法即可使用Product
和Purchase
实际实例,您需要将它们添加到LineCommand
类而不是compositePrimaryKey
。
Of course, your @Embeddable LineCommandId
class should then have no annotations as it is going to be used as @IdClass
(see below as to why Integer
is used instead of Long
here): 当然,您的@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;
}
}
What you need to make sure though, is that your base entity (for example, Product
) uses the name of the field from LineCommandId
in its mappedBy
attribute of @OneToMany
annotation. 你需要确保虽然什么,是你的基础机构(例如, Product
)使用从字段的名称LineCommandId
在它mappedBy
属性@OneToMany
注解。
Another thing I noticed was that you calculate subTotal
attribute on the fly. 我注意到的另一件事是您subTotal
计算subTotal
属性。 Since it is also not part of the database schema, that is the attribute you need to annotate with @Transient
, or you will get an exception about Hibernate being unable to find setter
. 由于这也不是数据库架构的一部分, 这就是你需要注释的属性@Transient
,或者你会得到关于Hibernate是一个例外unable to find setter
。
The NullPointerException
is most probably due to the fact your wrapper objects return null
from the get()
method on, say, idProduct
attribute, when the wrapper object is not initialized: NullPointerException
最有可能是由于未初始化包装器对象时,包装器对象从idProduct
方法(例如idProduct
属性get()
返回null
的事实:
public Long getIdProduct() {
return idProduct.get();
}
An even more probable NPE is where some of your getter methods use autoboxing to convert from a non-primitive to primitive: 甚至更可能的NPE是您的某些getter方法使用自动装箱从非原始类型转换为原始类型的地方:
public long getIdPurchase() {
return idPurchase.get();
}
If idPurchase.get()
returns null
, the autoboxing's implicit null.longValue()
will result in an exception. 如果idPurchase.get()
返回null
,则自动装箱的隐式null.longValue()
将导致异常。
Once you get past the initial problem of starting the persistence unit, you will also have noticed that Long
is fundamentally incompatible with SERIAL
, which you are using in your table: SERIAL
maps to INTEGER
whereas Long
maps to BIGINT
. 一旦克服了启动持久性单元的最初问题,您还将注意到Long
从根本上与表中使用的SERIAL
不兼容: SERIAL
映射到INTEGER
而Long
映射到BIGINT
。 You probably want to change that to match. 您可能想要更改它以使其匹配。 That is also why the above example LineCommandId
is using Integer
s. 这就是为什么上面的示例LineCommandId
使用Integer
的原因。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.