JoinColumn default behaviour - jpa

can you explain what is default behaviour of #JoinColumn annotation when it's applaied to a field without any other relation annotation.
#Entity
class Employee{
#Id int id;
#JoinColumn(name = "man_id")
Employee manager;
}
how does jpa proceed it? if i.e. employee is many side of onetomany biderectional relation with department, where fetchtype lazy specified on both sides, when I access(ask collection for size) getEmployees property of department entity, eclipcelink generates sql and selects all records from employee entity also.
SELECT ID FROM deps WHERE (ID = ?)
SELECT ID, name, man_id, dep_id FROM emps WHERE (dep_id = ?)
additional requests
SELECT ID, name, man_id, dep_id FROM emps WHERE (ID = ?)
SELECT ID, name, man_id, dep_id FROM emps WHERE (ID = ?)
SELECT ID, name, man_id, dep_id FROM emps WHERE (ID = ?)
.................
If I mark this property with #OnetoOne(which is actually i mean) relation and set fetchtype lazy, everything "works correct" meaning manager property is not fetched until I ask employee for it.
SELECT ID FROM deps WHERE (ID = ?)
SELECT ID, name, man_id, dep_id FROM deps WHERE (dep_id = ?)

By not putting a relationship type/annotation, actually means JPA would not treat your attribute as an entity.
Perhaps, you already had your schema in place that's why your queries are working. However, I experimented on this and started with a fresh database. The behavior is different from what you expected. With Hibernate, my chosen JPA provider (with hbm2ddl property set to create), it generated the ff. schema:
create table Employee (
id bigint not null auto_increment,
manager tinyblob,
name varchar(255),
primary key (id)
)
Since my manager is an Employee class implementing Serializable interface, it was by default mapped to a BLOB type column.
Also by not mapping it with any relationship annotations, means that there will be no foreign key constraints applied to your manager column.

Related

Child entity is not getting inserted/updated on parent entity update with onetomany

I am using #OneToMany annotation to save parent and child entities but I am facing issues while saving child entity in a particular case.
Child entity is getting saved in two cases:
During first insert of a parent with child.
During update of a parent with child when there was no child inserted/saved in database
in first insert
But When parent is inserted with child 1 and then during update of a parent I try to insert child 2 then I am not able to save the child 2
it is failing with below exception:
o.h.e.jdbc.spi.SqlExceptionHelper - ORA-01407: cannot update ("app_Name"."Child_entity"."REFERENCE_ID") to NULL\n
23:22:06.068 ERROR o.h.i.ExceptionMapperStandardImpl - HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
Please see my code as below:
#Data
#Entity
#Table("Parent_table")
public class Parent_entity implements Serializable {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval =true)
#JoinColumn(name="REFERENCE_ID")
private Set<Child_Entity> childrens ;
}
#Data
#Entity
#Table("child_table")
public class Child_entity implements Serializable {
#Id
#GeneratedValue(generator = "seq_gen", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "seq_gen", sequenceName = "child_SEQ",allocationSize=1)
#Column(name ="col_name")
private Integer asSeq;
#Column(name ="REFERENCE_ID")
private String referenceid;
}
In mapper class, I am explicitly setting primary key of the parent table.
Oracle database side I have below foreign key constraint added
ALTER TABLE child_table
ADD CONSTRAINT FK_parent_table
FOREIGN KEY (REFERENCE_ID)
REFERENCES Parent_table(REFERENCE_ID);
I have browsed through similar question on stackoverflow, which suggests that if you are using existing column for foreign key then existing values for that column should not be null.
But in my case column "REFERENCE_ID" is already non nullable.
Please let me know or suggest if I need to add something else to make it work.
Thank you.
Edit:
In update scenario, Hibernate is generating below query:
update child_table set reference_id=null where reference_id=? and child_seq=?
where reference_id is Parent's primary key and child_seq is Child's primary key
Any idea why hibernate is trying to update Parent's primary key
I am explicitly setting Parent Primary key's value in Child's entity
There are actually three problems here:
Apparently "update scenario" inserts two new children instead of keeping one and adding one.
Unidirectional OneToMany relationship with a non-nullable join column
Lombok-generated equals and hashCode
TL;TR: GOTO 2
1. Update scenario
Hibernate is trying to update reference_id to NULL because it wants to "detach" a child entity from the parent. That means, that during update, you are adding two new children instead of keeping one and adding one. I haven't seen the relevant piece of code of yours, but I assume it might look more or less like this:
ParentEntity parent = new ParentEntity();
parent.setId("test");
ChildEntity child1 = new ChildEntity();
child1.setReferenceid(parent.getId());
parent.setChildrens(new HashSet<>(Arrays.asList(child1)));
repository.save(parent);
ChildEntity child2 = new ChildEntity();
child2.setReferenceid(parent.getId());
parent.getChildrens().add(child2);
repository.save(parent);
It ends up with a ConstraintViolationException. In the second save call, child1 is still a "detached" instance,
its id is NULL and Hibernate treats both children as they were new. So first it adds them to the child_table and later tries to remove the "old" one,
by setting its referenceId to NULL (orphan removal hapens later, and is kind of unrelated).
It could be easily fixed:
// ...
parent = repository.save(parent); // <- save(parent) returns updated object
ChildEntity child2 = new ChildEntity();
child2.setReferenceid(parent.getId());
parent.getChildrens().add(child2);
repository.save(parent);
No exceptions anymore but it doesn't solve the problem. Sooner or later you are going to remove a child from the children set and
it will always result in an exception.
2. Unidirectional OneToMany relationship with a non-nullable join column
The canonical way of modeling it would be as follows:
ParentEntity
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#JoinColumn(name = "REFERENCE_ID", nullable = false)
private Set<ChildEntity> childrens;
ChildEntity
#Column(name = "REFERENCE_ID", insertable = false, updatable = false)
private String referenceid;
It should work but Hibernate will generate unnecessary 'update' queries:
select parententi0_.parent_id as parent_i1_1_1_, childrens1_.reference_id as referenc3_0_3_, childrens1_.id as id1_0_3_, childrens1_.id as id1_0_0_, childrens1_.name as name2_0_ ...
select nextval ('child_seq')
select nextval ('child_seq')
insert into child_table (name, reference_id, id) values (?, ?, ?)
insert into child_table (name, reference_id, id) values (?, ?, ?)
update child_table set reference_id=? where id=?
update child_table set reference_id=? where id=?
delete from child_table where id=?
Not a big deal with one or two items, but with 100?
This happens because the ParentEntity is the
owner of the relationship (due to the #JoinTable annotation). It knows nothing about child_table foreign key and doesn't know how to deal with it.
2b. "Half of" bidirectional OneToMany relationship
Alternatively, we can try to make ChildEntity the owner of the relationship by removing #JoinColumn and adding mappedBy. In theory, there should be a corresponding #ManyToOne on the other side of the relationship, but it seems to work without it. This solution is optimal, might be not portable though (to different JPA providers or different Hibernate versions).
ParentEntity
#OneToMany(mappedBy = "referenceid", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ChildEntity> childrens;
ChildEntity (no changes, same as in the question)
#Column(name = "REFERENCE_ID")
private String referenceid;
On update Hibernate generates following queires:
select parententi0_.parent_id as parent_i1_1_1_, childrens1_.reference_id as referenc3_0_3_, childrens1_.id as id1_0_3_, childrens1_.id as id1_0_0_, childrens1_.name as name2_0_ ...
select nextval ('child_seq')
select nextval ('child_seq')
insert into child_table (name, reference_id, id) values (?, ?, ?)
insert into child_table (name, reference_id, id) values (?, ?, ?)
delete from child_table where id=?
3. Lombok-generated equals and hashCode
This is not directly related to your question but I think you will face this problem sooner or later. You are using #Data annotations (I assume they are Lombok's, if not, ignore this section). They will generate equals and hashCode methods from all the fields by default, including ids. It is fine in the ParentEntity, where the id is set manually. But in the ChildEntity, where the id (aka asSeq) is generated by the database, it breaks the hashCode()/equals() contract. It may lead to really sneaky bugs. From Hibernate documentation:
The issue here is a conflict between the use of the generated identifier, the contract of Set, and the equals/hashCode implementations. Set says that the equals/hashCode value for an object should not change while the object is part of the Set. But that is exactly what happened here because the equals/hasCode are based on the (generated) id, which was not set until the JPA transaction is committed.
You may want to read more about it here:
Implementing equals() and hashCode()
The JPA hashCode() / equals() dilemma

JPA Inheritance: Query fails with unknown column

Executing the following JPA query while using EclipseLink v2.7.4:
SELECT pr FROM AbstractProduct pr WHERE pr.shelve.superMarket.id = :1 ORDER BY pr.sortOrder
Gives the following error:
Unknown column 't0.SORTORDER' in 'order clause'
Error Code: 1054
Call: SELECT t2.ID, t2.SORTORDER, t2.SHELVE_ID FROM APPLE t2, SHELVE t1 WHERE ((t1.SUPERMARKET_ID = ?) AND (t1.ID = t2.SHELVE_ID)) ORDER BY t0.SORTORDER
bind => [12]
The query is refering to t0 but nowhere in the generated query does it define which table t0 is.
These are the entities that I'm using:
#Entity
public class SuperMarket {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToMany(mappedBy = "superMarket")
List<Shelve> shelves;
}
#Entity
public class Shelve {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
protected SuperMarket superMarket;
#OneToMany(mappedBy = "shelve")
protected List<AbstractProduct> products;
}
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractProduct {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
protected Shelve shelve;
protected long sortOrder;
}
#Entity
public class Apple extends AbstractProduct {
}
#Entity
public class Banana extends AbstractProduct {
}
Which results in the following queries:
CREATE TABLE SUPERMARKET (ID BIGINT NOT NULL, PRIMARY KEY (ID));
CREATE TABLE SHELVE (ID BIGINT NOT NULL, SUPERMARKET_ID BIGINT, PRIMARY KEY (ID));
CREATE TABLE APPLE (ID BIGINT NOT NULL, SORTORDER BIGINT, SHELVE_ID BIGINT, PRIMARY KEY (ID));
CREATE TABLE BANANA (ID BIGINT NOT NULL, SORTORDER BIGINT, SHELVE_ID BIGINT, PRIMARY KEY (ID));
CREATE TABLE ABSTRACTPRODUCT (SHELVE_ID BIGINT);
The last table ABSTRACTPRODUCT should not be created since it is an Abstract Java Entity and I'm using the table per class inheritance style. This seems to be a bug in eclipselink it is also discussed in this question: Understanding of TABLE_PER_CLASS with keys in eclipselink It is the combination of the inheritance with the OneToMany relations that seems to trigger the create table statement. Not sure if this bug is related to the query error that I mentioned at the start. I think not as this table doesn't even have the sort order field.
When I remove the ORDER BY clause the query will execute succesfully. When I change the query to only go one level up it will also execute succesfully:
SELECT pr FROM AbstractProduct pr WHERE pr.shelve.id = :1 ORDER BY pr.sortOrder
For a test I got rid of the inheritance and let the Shelve entity have a OneToMany relation to Apple directly, where Apple did not extend any other class, in that case the query is also executed succesfully. But I need the abstract class and inheritance.
Any idea why the generated query is wrong in this case?
As is noted in an answer below I could use a different inheritance strategy to solve this problem in a different way. I choose the table per class type because that allows me to use the abstract entity in queries and the concrete classes get a table with all the fields in it. I was hoping that this helps with performance when doing a lot of inserts and selects from the concrete classes because that would only involve a single db table.
Update
I think this is an error in EclipseLink I have created two bugreports:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=549866 for the abstract class creating a table
https://bugs.eclipse.org/bugs/show_bug.cgi?id=549868 for the error in the query
I was able to reproduce your problem with the inheritance strategy TABLE_PER_CLASS. The query executed as expected once I changed it to InheritanceType.JOINED and recreated the schema of the database (in my case: PostgreSQL 10.9).
So the code should be changed to:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class AbstractProduct {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne
protected Shelve shelve;
protected long sortOrder;
}
Note well:
I added the missing #ManyToOne annotation to the attribute shelve.
#ManyToOne should also be added to protected SuperMarket superMarket; in the entity Shelve.
The resulting DB schema looks as follows:
CREATE TABLE public.abstractproduct
(
id bigint NOT NULL,
dtype character varying(31) COLLATE pg_catalog."default",
sortorder bigint,
shelve_id bigint,
CONSTRAINT abstractproduct_pkey PRIMARY KEY (id),
-- FK definitions left out for brevity
)
And Apple, for instance, becomes:
CREATE TABLE public.apple
(
id bigint NOT NULL,
CONSTRAINT apple_pkey PRIMARY KEY (id),
-- FK definitions left out for brevity
)
Hope it helps.

How exactly work the #OneToMany JPA annotation in this example? Is it related to a table column or an entity class field?

I have 2 DB tables named respectivelly T_ACCOUNT and T_ACCOUNT_BENEFICIARY.
These tables have the following structure:
create table T_ACCOUNT (ID integer identity primary key, NUMBER varchar(9), NAME varchar(50) not null, CREDIT_CARD varchar(16), unique(NUMBER));
create table T_ACCOUNT_BENEFICIARY (ID integer identity primary key, ACCOUNT_ID integer, NAME varchar(50), ALLOCATION_PERCENTAGE decimal(5,2) not null, SAVINGS decimal(8,2) not null, unique(ACCOUNT_ID, NAME));
And the T_ACCOUNT table is bound to the T_ACCOUNT_BENEFICIARY table with a one to many relationship, this is the graphical representation:
So this is the first class named Account that map the T_ACCOUNT table:
#Entity
#Table(name="T_ACCOUNT")
public class Account {
#Id
#GeneratedValue
#Column(name="id")
private Long entityId;
#Column(name="NUMBER")
private String number;
#Column(name="NAME")
private String name;
#OneToMany
#JoinColumn(name="ACCOUNT_ID")
private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();
#Column(name="CREDIT_CARD")
private String creditCardNumber;
// GETTERS & SETTERS
}
And this is the Beneficiary class that map the T_ACCOUNT_BENEFICIARY table:
/**
* A single beneficiary allocated to an account. Each beneficiary has a name (e.g. Annabelle) and a savings balance
* tracking how much money has been saved for he or she to date (e.g. $1000).
*/
#Entity
#Table(name="T_ACCOUNT_BENEFICIARY")
public class Beneficiary {
#Id
#GeneratedValue
#Column(name="ID")
private Long entityId;
#Column(name="NAME")
private String name;
#Embedded
#AttributeOverride(name="value",column=#Column(name="ALLOCATION_PERCENTAGE"))
private Percentage allocationPercentage;
#Embedded
#AttributeOverride(name="value",column=#Column(name="SAVINGS"))
private MonetaryAmount savings = MonetaryAmount.zero();
As you can see into the Account I have the beneficiaries field that implement the one to may relationship
#OneToMany
#JoinColumn(name="ACCOUNT_ID")
private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();
I know that, on the DB, this relationship is implemented by the ACCOUNT_ID field of the T_ACCOUNT_BENEFICIARY table (so multiple row of the T_ACCOUNT_BENEFICIARY table can have the same value of the ACCOUNT_ID field and this means that a single row of the T_ACCOUNT table can be associated to more than one rows of T_ACCOUNT_BENEFICIARY table).
As you can see in the previous sippet there is the #JoinColumn(name="ACCOUNT_ID") annotation.
My doubt is generated by the fact that I have an ACCOUNT_ID column on my T_ACCOUNT_BENEFICIARY table, infact:
create table T_ACCOUNT_BENEFICIARY (ID integer identity primary key, ACCOUNT_ID integer, NAME varchar(50), ALLOCATION_PERCENTAGE decimal(5,2) not null, SAVINGS decimal(8,2) not null, unique(ACCOUNT_ID, NAME));
but this column seems to not be mapped on the Beneficiary that map this T_ACCOUNT_BENEFICIARY table.
So my doubts is: the #JoinColumn(name="ACCOUNT_ID") is working at relational level performing the join operation on the ACCOUNT_ID column of the table mapped by the Beneficiary entity (T_ACCOUNT_BENEFICIARY) or am I missing something? How exactly is performed this join?
If my interpretation is right can I work at entity level and say to join the beneficiaries field of my Account entity class to a new accountId field inserted into my Beneficiary entity class and mapping the ACCOUNT_ID column of the T_ACCOUNT_BENEFICIARY table?
Tnx
It seems is a Unidirectional OneToMany relationship
In JPA 2.0 a #JoinColumn can be used on a OneToMany to define the foreign key
I'm not sure if I understand your question. But what you have done with your #JoinColumn annotation is correct and Hibernate will execute appropriate SQL statements when you have multiple beneficiaries for your account. For example executing multiple INSERTS if you have two Beneficiaries for an Account. And yes using the #JoinColumn annotation is at the hibernate level. If you want to access an Account from a Beneficiary entity you would need to define a Bidirectional relationship in the Beneficiary class like below.
#Entity
#Table("T_ACCOUNT_BENEFICIARY")
public class Beneficiary {
#ManyToOne(mappedBy = "beneficiaries")
Account account;
...
}

Entity Framework Query based on relationship

If you have database tables with relationships as follows:
Person
PersonId (PK), Name
PersonDoesService
PersonId (FK), ServiceId (FK)
Service
ServiceId (PK), Type
Is it possible with Entity Framework in one statement to get all Persons that does one service based on serviceId?
The code below will do what you are asking. Make sure that your ServiceId exists.
var people = context.Service.First(s => s.ServiceId == theServiceIdIWant).Person // IEnumerable<Person>

Accessing table with only foreign Keys in Entity Data Framework

i am new to Entity Data framework
i have tables
Actions ( Actionid (pk) ,Actionname , ... )
Roles ( Roleid(pk) , Rolename , .... )
ActionRoles( Actionid(pk,fk) , Roleid(fk) ) [Mapping Table]
Please Suggest me the LINQ to get the RoleNames for Perticular ActionID
(Note : there is No class created with Name ActionRoles in entitydesigner.cs as because it doesn't have any other column name then ActionId and RoleID )
Thank you in Advance
When you have a link table like this, adding all tables to the Entity Model should create 2 way relationship Properties between the 2 end tables, hiding the link table completely allowing you to access via something like:
IEnumerable<string> roleNames = Entities.Actions
.First(a => a.Actionid == actionid)
.Roles
.Select(r => r.Rolename);
where actionid is an int variable containing the actionid you're interested in.
For a discussion of how to handle many-to-many relationships such as this (both foreign keys must be in the ActionRoles primary key as indicated in the comment to your question), see these tutorials:
For EF 4.0: http://www.asp.net/entity-framework/tutorials/the-entity-framework-and-aspnet-–-getting-started-part-5
For Ef 4.1: http://www.asp.net/entity-framework/tutorials/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application