How to optionally persist secondary table in Eclipselink - jpa

I am working with Eclipselink and having issue with using secondary table.
I have two tables as below.
Student with columns student_id(Primary Key), student_name etc.
Registration with columns student_id(FK relationship with Student table), course_name (with not null constraint) etc.
The requirement is student may or may not have registration. If student has registration, the data should be persisted to Registration table as well. Otherwise only Student table should be persisted.
My code snippet is as below.
Student.java
------------
#Entity
#Table(name = "STUDENT")
#SecondaryTable(name = "REGISTRATION")
#Id
#Column(name = "STUDENT_ID")
private long studentId;
#Basic(optional=true)
#Column(name = "COURSE_NAME", table = "REGISTRATION")
private String courseName;
I tried the following scenarios.
1. Student with registration - Working fine. Data is added to both Student and Registration tables
2. Student without registration - Getting error such as 'COURSE_NAME' cannot be null.
Is there a way to prevent persisting into secondary table?
Any help is much appreciated.
Thanks!!!

As #Eelke states, the best solution is to define two classes and a OneToOne relationship.
Potentially you could also use inheritance, having a Student and a RegisteredStudent that adds the additional table. But the relationship is a much better design.

It‘s possible using a DescriptorEventListener. The aboutToInsert and aboutToUpdate callbacks have access to the DatabaseCalls and may even remove the statements hitting the secondary table.
Register the DescriptorEventListener with the ClassDescriptor of the entity. For registration use a DescriptorCustomizer specified in a Customizer annotation at the entity.
However, you will not succeed fetching the entities back again later on. EclipseLink uses inner joins when selecting from the secondary table, so that the row of the primary table will be gone in the results.

Related

Typed Query for those tables created from relationships (One to Many)

I am little confused, If i have two tables related then I will have a combined table in MySQL, Since we do not have a class in our project , how would my Typed Query look in order to fetch data from table that is create from Relationship( say One to Many).
eg:
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "CustomerBilling",joinColumns = #JoinColumn(name = "Customer_Id"),
inverseJoinColumns = #JoinColumn(name = "Billing_Id"))
private List<Billing> billing = new ArrayList<>();
with the above mentioned code i will have CustomerBilling table , So i would like to get all the records for a particular customer id. in my test (jUnit) file what Typed Query do i need to put ?
TypedQuery<Customer> a = em.createQuery("select b from CustomerBilling b where b.Customer_Id =?1", Customer.class);
This did not work Since CustomerBilling abstrate schema is not present.
Thanks
Prashanth
I am sure you have classes in your project as it is java based :)
Reading between the lines, you mean, that you have 3 tables in DB Customer, Billing and CustomerBilling, but only 2 entities in JPA the Customer and Billing as the CustomerBilling is there only to store the relation. (But your example of typed query tries to get Customer.class as the result so maybe I am wrong).
You don't reference jointable objects directly (unless they are mapped).
You can query by Customer, or by Billing but not by CustomerBilling. If there is no such entity defined in JPA, there is nothing that JPA can query about.
And you shouldn't try to do it, even for unit testing (BTW jpa tests are integration tests not unit tests).
The whole point of mapping the relationships with #OnteToMany and such is to hide, from the program, the actual DB structure. So you should access Billings of a customer, or customer for a billing, not the relation info.
Currently the relation is stored by join table, but it could be change it to join column and whole program logic would stay the same (and so should the tests).
You should test, that if you add bilings to customer and save them, you correctly retrieves them, that if you remove the biling from customer it disappears, but there is no reason to check the content of the join table.
Finally, if you really must, you can use native sql query to access CustomerBilling table
em.createNativeQuery
or
you can change your mappings, and introdude the CustomerBilling entity, and map your OneToMay on Customer to the CustomerBilling instead of Directly Billing.

Composite key with JPA entity, implementing tree of objects in one table?

I have one table named PLACES with one composite primary key (parent_id, version_id). It is a tree of objects, which are linked through the keys. One child has just one parent, and one parent may have many children.
How can I describe it with JPA entity?
Use a ManyToOne relation from the child to the parent.
This is for OpenJpa. Might even work.
public class Place{
#EmbeddedId
PlaceId id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="PARENT_ID" referencedColumnName="ID"), // ID = matching primary key
#JoinColumn(name="PARENT_VER" referencedColumnName="VER") //etc
})
public Place parent;
#OneToMany(fetch=FetchType.LAZY, mappedBy="parent")
public List<Place> childPlaces;
}
The OneToMany relation might be omitted if it's not needed. If I remember correctly, it needs to be managed, ie childs need to be inserted there too when creating child-places, by you, using java.
Btw.
I would advise against using a version column in a composite key in order to manually keep old versions of your data (for auditing or similar purposes) as that slows down and complicates all joins, and generally will make you miserable at some point in your life - As opposed to using a version column that is not part of a composite key, used for optimistic locking.
You might want to look into some kind of build in support for auditing/logging. OpenJpa has auditing support (OpenJPA Audit) and most database provide some support, either out-of-the-box or by using triggers. All alternatives are faster and better than using composite keys.

JPA Multiple relationships on one field in an entity

I am a beginner to using JPA 2.0 and databases in general and I was just confused about a few concepts.
So I have a total of 3 tables. One is the UserTable, which contains all the information about my user. It has a primary key field called user_Id. My other two tables are ExercisesTable and FoodIntakeTable, and they each have a foreign key field called user_Id to reference the user_Id in my UserTable. I want a one-to-many relationship from my user_Id table to each of the two tables so I can find pull out exercise information or food information for a user.
Pretty much like this:
FoodIntakeTable <-> UserTable <-> ExercisesTable
I need a bidirectional mapping from UserTable to FoodIntakeTable and also a bidirectional mapping from UserTable to ExercisesTable from the field user_Id.
The problem is, when I try to write my code like this in my Usertable class:
#OneToMany(mappedBy="ExercisesTable.userId")
#OneToMany(mappedBy="FoodIntakeTable.userId")
public long userId;
It's illegal because I can't have two #OneToMany annotations on the same field. I think it's supposed to be legal in a normal relational database and I'm just confused about how you translate this into JPA. I'm very new to the whole concept of databases and entities in general, so any help would be appreciated.
In JPA you can directly reference entity objects instead of the ids that they are mapped by. Try something like this:
You should have an entity type for each of your tables, say Exercise for ExercisesTable, FoodIntake for FoodIntakeTable, and User for your UserTable.
Then your User entity is the owning side of the relationships, having one field per relationship like this:
#OneToMany(mappedBy=...)
private List<Exercise> exercises;
#OneToMany(mappedBy=...)
private List<FoodIntake> foodIntakes;

How to design many-to-many relationship in JPA entities?

I am trying to understand what would be the better way to design 2 entities which has many-to-many relationship? In database there will be a connecting table between these two entities. But do i have to follow the same approach while creating my entities?
For example: User to User group
A user may belong to many group and a group may contain many user.
In relational database I will have 3 table like User, User2Group, Group
So when I am creating my JPA entities, should I have 3 entities for 3 table or just 2 entities by providing the proper annotation(#ManytoMany).
Since I am new to JPA, I am trying to understand good and bad side from following point of view:
Performance
Code maintenance
Thanks, you input will be greatly appreciated.
No, you don't need to map the join table as an entity. Just use the ManyToMany annotation:
public class User {
...
#ManyToMany
private Set<Group> groups;
}
public class Group {
...
#ManyToMany(mappedBy = "groups")
private Set<User> users;
}
You would only need to map the join table as an entity if it was not a pure join table, i.e. if it had additional information like, for example, the date when the user entered in the group.
The mapping has little incidence on the performance. What is important is how you use and query the entities, and how the database is designed (indices, etc.)

Why #OneToOne is allowing duplicate associations?

I have User and Address classes as follows:
class User
{
...
...
#OneToOne( cascade=CascadeType.ALL)
#JoinColumn(name="addr_id")
private Address address;
}
class Address
{
...
...
#OneToOne(mappedBy="address")
private User user;
}
Here my assumption is with this domain model I can associate one address to only one user(user.addr_id should be unique).
But with this domain model, I am able to create multiple users and associate them to the same address.
User u1 = new User();
//set data
Address addr1 = new Address();
//set data
u1.setAddress(addr1);
...
session.save(u1);
--> this is creating one address record with addr_id=1 and inserting one user record in user table with addr_id=1. Fine.
Again,
User u2 = new User();
//set data
Address addr1 = (Address) session.load(Address.class, 1);
//set data
u2.setAddress(addr1);
...
session.save(u2);
--> This is creating second user record and associating to existing address with addr_id=1, which is not desired.
I can use #OneToOne(..)#JoinColumn(name="addr_id", unique=true) to prevent this.
But what will be the difference of using #OneToOne rather than #ManyToOne(.., unique=true).
#OneToOne itself should impose "unique=true" condition..right?
-Siva
#OneToOne is annotating the Java to express the idea of the relationship between the two classes. If this was a #OneToMany then we'd have a collection instead. So reading the annotations we understand the realtionships, and the JPA runtime also understands those.
The actual policing of the one-to-one is performed in the database - we need the schema to have the uniqueness constraints. The #JoinColumn expresses how that relationship is manifest in the DatabaseSchema.This can be useful if we are generating the schema.
However in many cases we use bottom-up tools to generate the Java from the schema. In this case there's no actual need for the Java annotations to reflect the database constraints, from a Java perspective we just see the relationship.
An intelligent compiler might warn us if the semantics of our #JoinColumn doesn't match the #oneToOne, but I'm not sure whether current implementations do that.
OneToOne is an annotation describing the object model. It says what the cardinality of the association is. unique="true" is an indication to the "model-to-ddl" tool that there should be a unique cosntraint on this column. You can use such a tool, but you're also free to create and maintain the database schema yourself.
Whatever you choose to do, the JPA engine doesn't make a query each time the OneToOne association is modified to check that the cardinality is respected. All it does is assuming that the code is correct, and the unique constraint in the database will make sure that the cardinality is respected.