JPA OneToMany using Inheritance - java

I have the following classes:
Hardware.java
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Hardware{
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private long id;
private String serialnumber;
#OneToMany (mappedBy="hardware")
private List<Equipment> equipments;
}
Computer.java
#Entity
public class Computer extends Hardware {
private String hostname;
private String macAdress;
private String ipNumber;
public Computer(){
super();
}
public Computer(String serialnumber, String hostname,
String macAdress, String ipNumber) {
super(serialnumber);
this.hostname = hostname;
this.macAdress = macAdress;
this.ipNumber = ipNumber;
}
}
NetworkDeivce.java
#Entity
public class NetworkDevice extends Hardware {
private String hostname;
private String ipAdress;
public NetworkDevice(){
super();
}
public NetworkDevice(String serialnumber, String hostname,
String ipAdress) {
super(serialnumber);
this.hostname = hostname;
this.ipAdress = ipAdress;
}
}
And now the class that is mapped to the Hardware Class:
Equipment.java
#Entity
public class Equipment {
public Equipment(){
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String serialnumber;
private String info;
#ManyToOne (fetch=FetchType.EAGER)
#JoinColumn(name="HW_ID")
private Hardware hardware;
}
Now if I add a Equipment to a Computer it all works finde, but if I try to add Equipment to a NetworkDevice I get this Error:
Internal Exception:
Internal Exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`inventorytool_beta`.`EQUIPMENT`, CONSTRAINT `FK_EQUIPMENT_HW_ID` FOREIGN KEY (`HW_ID`) REFERENCES `COMPUTER` (`ID`))
Error Code: 1452
Call: INSERT INTO EQUIPMENT (INFO, SERIALNUMBER, HW_ID) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(domain.hardware.Equipment#600a620d)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
at main.dbTest(main.java:74)
at main.main(main.java:18)
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`inventorytool_beta`.`EQUIPMENT`, CONSTRAINT `FK_EQUIPMENT_HW_ID` FOREIGN KEY (`HW_ID`) REFERENCES `COMPUTER` (`ID`))
Error Code: 1452
Call: INSERT INTO EQUIPMENT (INFO, SERIALNUMBER, HW_ID) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(domain.hardware.Equipment#600a620d)
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:851)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:913)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`inventorytool_beta`.`EQUIPMENT`, CONSTRAINT `FK_EQUIPMENT_HW_ID` FOREIGN KEY (`HW_ID`) REFERENCES `COMPUTER` (`ID`))
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
So I think it's a problem with the inheritance... I'm not very good at JPA..
Thanks in advance.

Have a look at the schema that was generated. You should had three different tables for hardware, due to your inheritance strategy: one for Hardware, one for Computer and one fon NetworkDevice. The problem is that HW_ID can only reference one table. Here your JPA provider chose Computer, probably because it's first in alphabetical order, but it can't handle all three classes.
Consider using another inheritance strategy, like JOIN.

Check the code where you insert Equipment to a NetworkDevice. It looks like HW_ID does not exists in the master-table(Hardware). Because it is trying to insert nonexistent Master-Table HW_ID as foreign-Key in child table(Equipment).
Regards,
Ravi

Yes it comes from your inheritance strategy. You can't use #GeneratedValue(strategy=GenerationType.IDENTITY) with table_per_class.
See Java/Hibernate JPA: InheritanceType.TABLE_PER_CLASS and IDs

Related

Eclipselink - OneToOne Mapping

I am currently developing a little Group Management System and using java 8 with eclipselink and SqlLite.
#Entity
#Table(name = "T_GROUP")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private List<Permission> permissions;
private boolean defaultGroup;
private String prefix;
private String suffix;
private int rank;
private boolean build;
#OneToOne
private Group inherit;
/**
* Instantiates a new Group.
*/
public Group() {
}
...Getters Setters
}
The Field inherit references to another group that has other permissions, but the current group inherits all permissions from the other group.
How do I work with the field inherit? It is the same Type.
The OneToOne relation gives me following error:
Internal Exception: java.sql.SQLException: [SQLITE_ERROR] SQL error or missing database (near "CONSTRAINT": syntax error)
Error Code: 0
Call: ALTER TABLE T_GROUP ADD CONSTRAINT FK_T_GROUP_INHERIT_ID FOREIGN KEY (INHERIT_ID) REFERENCES T_GROUP (ID)
If your question is how to map the self referencing relationship, you can simply use #OneToOne annotation. This is already answered here JPA: How to have one-to-many relation of the same Entity type

Trying Derived Identifiers using #JoinedColumns, got error Attempt to modify an identity column

This is my sql table structure:
create table TBL_EMPLOYEE_FIVE(
EMP_ID integer generated always as identity(start with 50, increment by 4),
NAME varchar(50),
COUNTRY varchar(50),
MGR_ID integer,
MGR_COUNTRY varchar(50),
constraint PK_COMPOSIT_001AD primary key(EMP_ID,COUNTRY),
constraint FK_COMPO_00123 foreign key(MGR_ID,MGR_COUNTRY) references TBL_EMPLOYEE_FIVE
)
And this is my entity mapping:
#Entity
#Table(name="TBL_EMPLOYEE_FIVE")
#IdClass(EmployeeId.class)
public class EmployeeOne implements Serializable{
public EmployeeOne(){}
public EmployeeOne(String employeeName,String empCountry){
this.empCountry = empCountry;
this.employeeName = employeeName;
}
public EmployeeOne(String employeeName,String empCountry,EmployeeOne manager){
this.empCountry = empCountry;
this.employeeName = employeeName;
this.manager = manager;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="EMP_ID")
private Integer employeeId;
#Id
#Column(name="COUNTRY")
private String empCountry;
#Column(name="NAME")
private String employeeName;
#ManyToOne( cascade= {CascadeType.PERSIST, CascadeType.PERSIST},
fetch= FetchType.LAZY,
targetEntity=EmployeeOne.class)
#JoinColumns({
#JoinColumn(name="MGR_ID",referencedColumnName="EMP_ID"),
#JoinColumn(name="MGR_COUNTRY",referencedColumnName="COUNTRY")
})
private EmployeeOne manager;
#OneToMany(cascade={CascadeType.PERSIST, CascadeType.PERSIST},mappedBy="manager")
private Set<EmployeeOne> employees;
// getters and setters,
}
This is the the embedded id mapping,
#Embeddable
public class EmployeeId implements Serializable{
public EmployeeId(){}
public EmployeeId(Integer employeeId,String empCountry){
this.employeeId = employeeId;
this.empCountry = empCountry;
}
#Column(name="EMP_ID")
private Integer employeeId;
#Column(name="COUNTRY")
private String empCountry;
// only getters and implementation of hashcode and equals method
}
And this is what I am trying to run in my main method:
EmployeeOne manager = new EmployeeOne("Yousuf Ahmadinejad", "IRAN");
em.persist(manager);
But here i am getting an exception i.e.
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Attempt to modify an identity column 'EMP_ID'.
It's not like i didn't understood the exception,
but why this exception occured in the first place? I already annotated it with #GenerateValue for Empid and I am not setting the empId manually. Does this exception occur because I have combined primary key as empId and country, and than the empId is autogenerated using Identity, hence its giving an exception ?
Can you please tell me whats going wrong
One more thing i want to add here is, if i removed #Column and #Embeddeble annotation for EmployeeId.java, and than run, i get an following exception,
Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.entities.derived.EmployeeId.employeeId
So just trying to find the solution to persist employee keeping the autogenerated Id as it is
First Hibernate does not generate id's for composite keys, so you should change EmployeeOne to:
#Id
//#GeneratedValue(strategy=GenerationType.IDENTITY) remove this line
#Column(name="EMP_ID")
private Integer employeeId;
Second that's not how you should implement EmployeeId composite key class. See: https://stackoverflow.com/a/3588400/1981720
Third, the exception is thrown by the database, not Hibernate. Check if you're getting the same exception with another database.

error while trying to add new value for JPA table

Im using JPa API's and its work well ,I have tried to add new member/column to the class(table) and when I was tried to add data for it works fine but in the commit part I get dump with the following error
Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: 'DOUBLE1' is not a column in table or VTI 'TEST.PERSON'.
Error Code: 20000
Call: INSERT INTO PERSON (ID, DOUBLE1, FIRSTNAME, LASTNAME, NONSENSEFIELD) VALUES (?, ?, ?, ?, ?)
bind => [5 parameters bound]
But in the table person I have added the member double1 as follows
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private String id;
private String firstName;
private String lastName;
private double double1;
....
public double getDouble1() {
return double1;
}
public void setDouble1(double double1) {
this.double1 = double1;
}
What am i missing here?
There is obviously no column DOUBLE1 in the database table VTI 'TEST.PERSON'. Adding a new field to a JPA entity does not automatically make it appear in the database as well.

Invalid insert order when use #Inheritance JPA attribute

I have Eclipselink persistence provider tuned on DB2 DB. Where is 3 tables which simplified definition are listed below:
CREATE TABLE root
(
id CHAR(32) NOT NULL PRIMARY KEY,
rec_type VARCHAR(20)
);
CREATE TABLE derived
(
id CHAR(32) NOT NULL PRIMARY KEY,
...
);
ALTER TABLE derived ADD CONSTRAINT fk_derived_to_root FOREIGN KEY (id) REFERENCES root(id);
CREATE TABLE secondary
(
derived_id NOT NULL PRIMARY KEY,
...
);
ALTER TABLE secondary ADD CONSTRAINT fk_secondary_to_derived FOREIGN KEY (derived_id) REFERENCES derived(id);
Java entity classes for these entities are listed below,
RootEntity:
#javax.persistence.Table(name = "ROOT")
#Entity
#DiscriminatorColumn(name = "REC_TYPE")
#Inheritance(strategy = InheritanceType.JOINED)
public class RootEntity {
private String id;
#javax.persistence.Column(name = "ID")
#Id
#GeneratedValue(generator = "system-uuid")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
private String principalType;
#Column(name = "PRINCIPAL_TYPE")
public String getPrincipalType() {
return principalType;
}
public void setPrincipalType(String principalType) {
this.principalType = principalType;
}
...
}
DerivedEntity:
#javax.persistence.Table(name = "DERIVED")
#Entity
#DescriminatorValue("DERIVED")
public class DerivedEntity extends RootEntity {
private SecondaryEntity secondaryEntity;
#OneToOne(mappedBy = "derived_id")
public SecondaryEntity getSecondaryEntity() {
return secondaryEntity;
}
public void setSecondaryEntity(SecondaryEntity secondaryEntity) {
this.secondaryEntity = secondaryEntity;
}
...
}
I see no derived table insertion in the test logs:
--INSERT INTO ROOT (ID, REC_TYPE) VALUES (?, ?)
bind => [241153d01c204ed79109ce658c066f4c, Derived]
--INSERT INTO SECONDARY (DERIVED_ID, ...) VALUES (?, ...)
bind => [241153d01c204ed79109ce658c066f4c, ...]
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.ibm.db2.jcc.am.fo: DB2 SQL Error: SQLCODE=-530, SQLSTATE=23503, SQLERRMC=SCHEM.SECONDARY.FK_SECONDARY_TO_DERIVED, DRIVER=3.57.82
So question is: why Eclipselink don't insert new record into DERIVED table prior to insertion to SECONDARY table?
P.S. Everything is working fine when no SECONDARY table (ROOT and DERIVED tables only) or no inheritance used (DERIVED tables generates id).
For inheritance JPA assumes the foreign key constraints in related table refer to the root table.
You can change your constraint to refer to the root table, or,
use a DescriptorCustomizer to set,
descriptor.setHasMultipleTableConstraintDependecy(true);
or,
customizer the OneToOneMapping to have its foreign key refer to the secondary table (JPA annotation always make it refer to the root table).
Please log a bug though, as JPA join columns should allow you to define a foreign key to the secondary table.
The reason that EclipseLink does defer the insert into the secondary table is to allow inserts to be grouped by tables to allow batch writing and avoid database deadlocks.

Hibernate insert with single column table problem

I'm using Hibernate with a SQLite database. I have the following class :
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class Authority {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int idAuthority;
(...)
I then have a class Author, that extends Authority and add 4 or 5 fields.
When I try to save an Author object, Hibernate generates the following request :
Hibernate: insert into Authority values ( )
And sqlite doesn't like that.
If I add a dummy String field, like "private String test" and set this property in the constructor, everything works fine.
I'm quite new to hibernate, so I'm not sure how to proceed. Do you have any idea ?
Edit : As requested, the mapping of the Author class :
#Entity
#Table(name="Authors")
#PrimaryKeyJoinColumn(name="idAuthority")
public class Author extends Authority {
#Column(name = "firstName")
protected String firstName;
#Column(name = "lastName")
protected String lastName;
#Column(name = "alias")
protected String alias;
(...)
}
Edit : As requested (bis), the insert code :
public void save(Object data) {
currentSession.saveOrUpdate(data);
}
Nothing fancy...
And to give you more possible leads, here is the database schema :
create table Authority (idAuthority integer, primary key (idAuthority))
create table Authors (alias varchar, firstName varchar,
lastName varchar, idAuthority bigint not null, primary key (idAuthority))
N.B. : in SQLite, an integer that is primary key is automatically set to AUTO-INCREMENT.
The Exception raised is this one :
java.sql.SQLException: near ")": syntax error
The request should be more like : insert into Authority values (NULL) to let SQLite do its auto-increment, not this weird "insert into Authority values ()".
Edit : This is definetely a problem with the SqlLite for Hibernate package. I just tried with Hsqldb, and it gives me a proper query :
insert into Authority (idAuthority) values (default)
(Then it fails too, but for very different reasons :p ).
I'm not sure there is a solution to this problem... other than using a different DB.
If you use InheritanceType.JOINED your table associated with class Authority must contain column associated with idAuthority and your table associated with class Author must contain column associated with idAuthority that is a foreign key to the primary identifier in table which presents Authority. It's required for table relations accociation
Try Overriding the ID.
#Entity
#Table(name="Authors")
#PrimaryKeyJoinColumn(name="idAuthority")
#AttributeOverride(name="idAuthority", column=#Column(name="id"))
public class Author extends Authority {
#Column(name = "firstName")
protected String firstName;
#Column(name = "lastName")
protected String lastName;
#Column(name = "alias")
protected String alias;
(...)
}

Categories

Resources