JoinColumns behavior for queries - jpa

I'm trying to understand if I can effect the sql query constructed by eclipselink based on my domain object's (Organization) #JoinColumns on a self-referencing Parent field.
#JoinColumns({
#JoinColumn(name = "PARENT_ID"),
#JoinColumn(name = "ORG_ID", referencedColumnName = "ORG_ID", nullable = false, updatable = false, insertable = false)
})
private Organization parent;
Reason for doing it is the query is faster when the join has both the parent_id and org_id.
Everything is fine until we try to query to find top-level organizations (Parent = null). The generated query has parent_id is null and org_id is null... this doesn't give us what we need. What am I doing wrong?

Related

JPA one-to-one with secondary table for root entity

Following situation:
An root-Entity is build up from two database tables.
#Table("A")
#SecondaryTable(name="B", pkJoinColumns = {
#PrimaryKeyJoinColumn(name = "key1", referencedColumnName = "key1"),
#PrimaryKeyJoinColumn(name = "key2", referencedColumnName = "key2")})
The root entity contains an one-to-one association to an other entity lets call it "sub"
#OneToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "key1", referencedColumnName = "key1", updatable = false, insertable = false),
#JoinColumn(name = "KVSTU", referencedColumnName = "KVSTUFE", updatable = false, insertable = false),
#JoinColumn(name = "CODE_TARF_GEN", referencedColumnName = "TARIFGENERATION", updatable = false,
insertable = false)
})
private Sub sub;
Every thing works fine, but the JPA provider (in my case EclipseLink) doen't create an (outer) join. Instead of a join, for every dependend sub-entity an extra query is created.
I also tried pk join columns instead of join columns for the one-to-one association, with the same result.
What am I doing wrong?

JPA JoinColumns behaviour

class Employee {
#EmbeddedId
private EmployeeId id; // composite key with two fileds companyId and employeeNumber
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL)
private List<Phone> phoneList;
}
class Phone {
#EmbeddedId
private PhoneId phoneId; // composite key with three fileds companyId , employeeNumber and phoneNumber
#ManyToOne
#JoinColumns({
#JoinColumn(name = "COMPANY_ID", referencedColumnName = "COMPANY_ID", insertable = false,
updatable = false),
#JoinColumn(name = "EMPLOYEE_NUMBER_NEW", referencedColumnName = "EMPLOYEE_NUMBER",
insertable = false, updatable = false)})
private Employee employee;
}
In above example, Employee table gets generated with column employeeId, employeeNumber. And Phone table gets generated with employeeId, employeeNumber, phoneNumber, employee_nuber_new.
When I am creating employee object and phone object, at the end employeeId and employeeNumber values in Phone table gets populated from Employee object ( as part of FK ) but employee_number_new column still have "null" value. I want to updated employee_number_new column with employeeNumber from Employee table.
Tried lot of ways including JoinFormula and all but no success. Can anybody throw some light on this ?
Thanks...
The reason you are getting a null value in the Phone table because you are using insertable = false in your JoinColumn definition. If you could set this to true then your problem would be solved.
Hibernate doesn't support the mixing of insertable and non-insertable columns. So you have to make companyId insertable too. But that leads to the possibility of your primary key being changed in the Phone table which you don't want.
So, we are left with two options, if the EMPLOYEE_NUMBER is the same in both tables don't create the new column EMPLOYEE_NUMBER_NEW column and use EMPLOYEE_NUMBER.
But if EMPLOYEE_NUMBER is not the same in both tables then and you need to save the employee number from employee object, create another insertable column it can be COMPANY_ID_EMP and make updateable false. That way when you save a phone object both EMPLOYEE_NUMBER_NEW and COMPANY_ID_EMP will be saved. But in practice COMPANY_ID_EMP and COMPANY_ID will be the same value.
The following code worked for me
#ManyToOne
#JoinColumns({
#JoinColumn(name = "companyIdEmp", referencedColumnName = "companyId", updatable = false),
#JoinColumn(name = "employeeNumberNew", referencedColumnName = "employeeNumber", updatable = false)})
private Employee employee;

unidirectional OneToMany mapping problems

I'm facing some problems with unidirectional onetomany mapping.
I got tables Users and Rubrica:
User (
scode double precision NOT NULL,
...
CONSTRAINT utenti_pkey PRIMARY KEY (scode)
)
Rubrica (
id serial NOT NULL,
id_owner integer NOT NULL,
id_contact integer NOT NULL,
CONSTRAINT rubrica_pkey PRIMARY KEY (id ),
CONSTRAINT rubrica_fk01 FOREIGN KEY (id_owner)
REFERENCES users (scode) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT rubrica_fk02 FOREIGN KEY (id_contact)
REFERENCES users (scode) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Don't mind about Users double PKey. It's a customer's table and I can't modify it. Rubrica store relation between its owner, a User, and contacts, a set of User too.
User is mapped as follow:
#SuppressWarnings("serial")
#Entity
#Table(name = "utenti", schema = "public")
public class User implements Serializable {
#Id
#Column(name = "scode", unique = true, nullable = false)
private Integer scode;
...
}
Ok. Here is when problems come. If I map Rubrica like this:
public class Rubrica2 implements Serializable {
#Id
#Column(name = "id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_owner", nullable = false, unique = true, referencedColumnName = "scode")
private User owner;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "id_contact", nullable = false, updatable = false, insertable = true, referencedColumnName = "scode")
private Set<User> relations = new HashSet<User>();
...
}
JBoss gave me this exception at deploy:
Caused by: org.hibernate.MappingException: Unable to find column with logical name: scode in org.hibernate.mapping.Table(public.rubrica) and its related
supertables and secondary tables
If I map Rubrica this way:
public class Rubrica2 implements Serializable {
#Id
#Column(name = "id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_owner", nullable = false, unique = true, referencedColumnName = "scode")
private User owner;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "scode")
private Set<User> relations = new HashSet<User>();
...
}
I got bad behavior at runtime. If I run this code
r = new Rubrica2();
q2.setParameter("id", ownerID);
User owner = (User) q2.getSingleResult();
r.setOwner(owner);
q2.setParameter("id", contactID);
User u = (User) q2.getSingleResult();
r.getRelations().add(u);
entityManager.persist(r);
I got this exception:
Hibernate: insert into public.rubrica (id_owner) values (?)
11:08:19,440 DEBUG [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (EJB default - 7)
ERROR: null values in column "id_contact" violates not-null constraint [n/a]: org.postgresql.util.PSQLException:
ERROR: null values in column "id_contact" violates not-null constraint
I follow theory indicated here about onetomany unidirectional.
I'm using JPA2.0, Hibernate4 (as provided by JBoss7.1.1.Final) and PostgresSQL.
This mapping, or the database design, makes no sense. If you want one Rubrica to have many contacts, you can't have a foreign key to the relation in the rubrica table. A foreign key can only reference one contact, not many.
To map such a one-to-many association, you would need a foreign key in user to rubrica (all the users having the same rubrica_id would be the contacts of this rubrica), or a join table between both tables.

data not saved on join table

I generated entities using NetBeans (EclipseLink JPA) based on the following diagram:
The relevant generated code is:
Course
#JoinTable(name = "course_software", joinColumns = {
#JoinColumn(name = "course_id", referencedColumnName = "id", nullable = false)}, inverseJoinColumns = {
#JoinColumn(name = "software_id", referencedColumnName = "id", nullable = false)})
#ManyToMany
private List<Software> softwareList;
Software
#ManyToMany(mappedBy = "softwareList")
private List<Course> courseList;
The problem is with Course entity. When I try to persist it as follows
getFacade().create(currentCourse);
currentCourse.setSoftwareList(selectedSoftware);
then a record is created in course, but nothing is created in course_software join table.
How is this caused and how can I solve it?
It seems you don't need to specify the referencedColumnName attributes of #JoinColumn annotations as long as the columns referenced in the joined table are already primary keys.

JPA join column only for insert/delete , disable select

I have #OneToMAny realationship inside my entity.
Is it possible to disable jpa from generating select for the joined column? beacuse I have many records in my main table and when selecting them , each record performs select for the joined column. I would like to disable this select is that possible?
UPDATE:
I tried inserting fetch LAZY but it still creates a select for Table02f and Table03f...
This is my code:
public class Table01f implements Serializable {
#OneToMany(fetch=FetchType.LAZY , cascade = CascadeType.ALL, mappedBy = "table01f")
private List<Table02f> table02fList;
//getter & setter...
}
public class Table02f implements Serializable {
#JoinColumn(name = "F2PRP", referencedColumnName = "F1PRP", insertable = false, updatable = false)
#ManyToOne(optional = false)
#JsonIgnore
private Table01f table01f;
#OneToMany(fetch=FetchType.LAZY , cascade = CascadeType.ALL, mappedBy = "table02f")
private List<Table03f> table03fList;
//getter & setter...
}
public class Table03f implements Serializable {
#JoinColumns({
#JoinColumn(name = "F3PRP", referencedColumnName = "F2PRP", insertable = false, updatable = false),
#JoinColumn(name = "F3BRN", referencedColumnName = "F2BRN", insertable = false, updatable = false)})
#ManyToOne(optional = false)
#JsonIgnore
private Table02f table02f;
//getter & setter...
}
Thank's In Advance.
Just add the fetch type LAZY to your #OneToMany relationship:
#OneToMany(fetch=FetchType.LAZY)
When you load the list of your main entities, JPA won't populate your list for this relationship, avoiding the generation of the SELECT.
Just have a look at this functionality in JPA documentation so that you can understand how to use it.
If you don't need the data make it LAZY (in general always make everything LAZY).
If you need the data, then you can use batch fetching, or join fetching.
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html