JPA mapping - composite FK with date-range - java

I am trying to solve following problem:
I have one table of ORDERS that contains: group of goods, type of goods and order date...this refers to table with GOODS, where group, type and valid-from is the PK + valid-to field.
I had a dream, that I can map goods to orders using JPA to save multiple SQL queries.
I believe I can basically use following aproach to map the composite relationship:
#JoinColumns({
#JoinColumn(name = "heilgruppe", referencedColumnName = "heilgruppe"),
#JoinColumn(name = "code", referencedColumnName = "heilmittel_code")
})
But I have troubles with the date attribute...because I obviously cannot just use "equals" to map one date field to another...I need to select such goods, where date of the order lies between valid-from and valid-to dates
But how to do it with JPA? Is it even possible to do it? Or am I forced to create new SQL query later in code to retrive required info?

Don't use composite PK in GOODS, but a technical ID (maybe generated) and you will avoid that problem and use this PK in ORDERS.
Or use the all GOODS composite PK in ORDERS and add valid-from in ORDERS (but it not the best way).

Related

JPA #OneToMany with 1 - 1..* relationship

How to properly map #OneToMany relationship where to create entity, on #One side of #OneToMany relationship, it is required to have atleast one entity from #Many side but entity on #Many side also requires entity on #One side to exist? To put this nightmare of a sentence simply, this is the scenario I have:
This is what I want:
[ENTITY A] 1 <-----> (1..*)[ENTITY B]
At the moment I have this:
[ENTITY A] 1 <-----> (0..*)[ENTITY B]
Which is easily done like this.
#OneToMany(cascade=CascadeType.ALL, mappedBy="customer")
public Set<Agreement> agreements = new HashSet<>();
and
#ManyToOne
#JoinColumn(name = "CUSTOMER_ID", nullable=false)
private Customer customer;
So the problem is my CUSTOMER table has no column corresponding to AGREEMENT table therefore I can't enforce rule of creating Customer only when Agreement is given. At the moment I can only setup rule to create Agreement when Customer is given because AGREEMENT table has column corresponding to CUSTOMER tabel, which is easily done by nullable=false condition.
It depends very much on what type of relationship you want to enforce. If the Agreement can exist independently from the Customer then this mean that the customer_id in agreement must be nullable.
If the Agreement can not exist independently this presumes that the customer id is not nullable in which case the Agreement can not be created in first place without the customer being created. This mean you have stronger association in between the customer and the corresponding Agreement.
Once we define that we have a relationship that is strong we need to investigate how strong it really is and who will own whom. Normaly the it is the Many side that owns the relationship and the updates are happening through the many side. This mean that your JoinColumn needs to be on the MANY and the mapped by needs to be on the ONE side.
It is interesting case when the ownership is inverse when the ONE side actually owns the relationship in this case the foreign key on the many side can not be NULL because there is no way for the owning ONE side to know what the MANY side key is.
JPA doesn't provide a way to validate this, but Hibernate Validator does:
#NotNull
#Size(min=1)
public Set<Agreement> agreements = new HashSet<>();
Then you have to manually test it via the Validator:
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
validator.validate(customer)

How Hibernate sorts collection with #OrderColumn annotation?

As I can see from generated SQL while querying entities with collection mapped like this:
#ElementCollection(targetClass = Long.class, fetch = FetchType.EAGER)
#CollectionTable(name = "LIST_GAMES",
joinColumns = #JoinColumn(name = "LIST_ID", referencedColumnName = "ID"))
#Column(name = "GAME_ID")
#OrderColumn(name = "GAME_ORDER")
private List<Long> gameIds;
Hibernate doesn't add any 'order by' statements to it. So does it mean, that collection is sorted in-memory after loading? Or does it means that I need a complex index on db level like (LIST_ID, GAME_ORDER) to handle order by in the db?
UP: My question is actually HOW hibernate sorts this collection? Is It handled by hibernate in-memory or its retrieved already sorted from db? And am I need to create database index for GAME_ORDER column?
OrderBy adds an order by clause to the generated SQL to order the members of the retrieved collection by a column of the table of the target entity:
#OrderBy("GAME_ORDER ASC")
public List<Long> gameIds;
will generate a SQL query like
select ... order by game.game_order ASC
#OrderColumn defines the name of an additional column in the table, containing the index of the entity in the list.
If you change the ordering of the elements in the list, Hibernate will change the value of this column. And if your gameId have 0, 3, and 5 as values in this column, the list will be
[gameIds0, null, null, gameIds3, null, gameIds5]
see here
As per Oracle Doc #OrderColumn is:
Specifies a column that is used to maintain the persistent order of a
list. The persistence provider is responsible for maintaining the
order upon retrieval and in the database. The persistence provider is
responsible for updating the ordering upon flushing to the database to
reflect any insertion, deletion, or reordering affecting the list.
So it is likely that the collection is sorted after the data is retrieved from the database and it is done in memory.

Join Entity with table that has no entity assosiated in JPQL

I have a table that has no associated Entity. I need to write JPQL query which will filter by that table's fields. Is it possible?
I know that I can do one of the following:
Create an Entity for that table( but it's basically a join table, so it will look strange to create an Entity class for it)
Write a native query( I don't like this approach either. If I use JPA, I must use JPQL only).
Create fully functional ManyToMany mapping( I just don't need it).
Could there be another approach?
Unfortunately you cannot do that with JPQL.
You should use SQL.
But a native query can also return Entities. Either if the returned values matches the entity or using #SqlResultSetMapping as described here:
http://javaee.support/sample/jpa-native-sql-resultset-mapping/
If you only need to join to entities on a relationship that is not mapped JPA 2.1 is able to JOIN on any table columns.
The problem was that I did not need a real many-to-many object mapping but only collection of id's in my only entity. So I came to following solution:
#ElementCollection
#CollectionTable(
name="user_to_feed",
joinColumns = #JoinColumn(name = "feed_id",referencedColumnName = "id")
)
#Column(name="user_id")
private List<Integer> userIds = new ArrayList<>();
This allows me to make following query:
select f.url from Feed f join f.userIds u where :id in u

Hibernate re-writes many to many entires?

I have a domain object that holds a collection of another object through a #ManyToMany annotaion:
#ManyToMany(fetch=FetchType.LAZY,cascade = { CascadeType.MERGE, CascadeType.PERSIST})
#JoinTable(name = "join_table", joinColumns=#JoinColumn(name="a_id", referencedColumnName = "a_id"), inverseJoinColumns=#JoinColumn(name = "b_id", referencedColumnName = "b_id"))
private List<B> BsList;
In the join table i hold additional data columns.
I noticed that when i work with the object that holds the list and call setBsList() the data i had in the additional columns is deleted.
Does Hibernate re-write the rows in the join table each time?
In the join table i hold additional data columns.
If you have additional data columns, it's not a join table. It's a table with two FKs which can also be PKs. And Hibernate is doing the right thing. So, you should instead create another entity representing this "fake join table", and map it accordingly.
You shouldn't call setBsList(), you should directly modify getBsList() otherwise Hibernate has no way of tracking what has changed.
When loading, the actual List you get is a Hibernate-specific implementation which deals with the lazy loading and tracks what is added and removed, so that it can be correctly updated. If you replace this with a different list implementation it will delete the old one and only add then only add the new entries. When using Hibernate with collections, it's a good idea to make the setter protected so you can't accidentally do this.

Hibernate #OneToOne mapping with a #Where clause

Will this work -
#OneToOne()
#JoinColumn(name = "id", referencedColumnName = "type_id")
#Where(clause = "type_name = OBJECTIVE")
public NoteEntity getObjectiveNote() {
return objectiveNote;
}
This is what I am trying to do - get the record from table note whose type_id is the id of the current object and type_name is OBJECTIVE.
I can't get the above mapping to work. What am I doing wrong here?
This just plain does not work, sorry :( You will need to do it as one to many and live with getting a collection with a single element.
If you really want it to work this way, you can trick hibernate by storing both the foreign key ID and the type_name in a join table and telling it that both columns make up the foreign key.
Actually you can achieve this by specifying #OneToOne without any #Where, but putting #Where on the referenced entity class. I tested this on Hibernate 4.3.11.
This works if you don't care about any entity objects that do not match your #Where.
If you do care about other entities, you can probably create a subclass entity, put #Where on it and join that subclass. But I have not tested this scenario.

Categories

Resources