Hibernate #OneToOne mapping with a #Where clause - java

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.

Related

JPA mapping - composite FK with date-range

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).

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

Difference between #OneToMany and #ElementCollection?

What is the difference between using a #OneToMany and #ElementCollection annotation since both work on the one-to-many relationship?
ElementCollection is a standard JPA annotation, which is now preferred over the proprietary Hibernate annotation CollectionOfElements.
It means that the collection is not a collection of entities, but a collection of simple types (Strings, etc.) or a collection of embeddable elements (class annotated with #Embeddable).
It also means that the elements are completely owned by the containing entities: they're modified when the entity is modified, deleted when the entity is deleted, etc. They can't have their own lifecycle.
I believe #ElementCollection is mainly for mapping non-entities (embeddable or basic) while #OneToMany is used to map entities. So which one to use depend on what you want to achieve.
#ElementCollection allows you to simplify code when you want to implement one-to-many relationship with simple or embedded type. For instance in JPA 1.0 when you wanted to have a one-to-many relationship to a list of Strings, you had to create a simple entity POJO (StringWrapper) containing only primary key and the String in question:
#OneToMany
private Collection<StringWrapper> strings;
//...
public class StringWrapper {
#Id
private int id;
private String string;
}
With JPA 2.0 you can simply write:
#ElementCollection
private Collection<String> strings;
Simpler, isn't it? Note that you can still control the table and column names using #CollectionTable annotation.
See also:
Java Persistence/ElementCollection
Basic or Embedded: #ElementCollection
Entities: #OneToMany or #ManyToMany
#ElementCollection:
the relation is managed (only) by the entity in which the relation is defined
table contains id reference to the owning entity plus basic or embedded attributes
#OneToMany / #ManyToMany:
can also be managed by the other entity
join table or column(s) typically contains id references only
#ElementCollection marks a collection. This does not necessarily mean that this collection references a 1-n join.
ElementCollection can override the mappings, or table for their collection, so you can have multiple entities reference the same Embeddable class, but have each store their dependent objects in a separate table.
#ElementCollection
This annotation will be applied when there is a relation with non-entity and these associations relation was HAS-A. Every collection is created with a table and gets relation by Foreign Key.
There are two types of element collections
Index (List, Map)
Non-Index (Set)
Index: The index type collection has a table with 3 columns they are
Key Column (Foriegn Key)
Index Column (Position of data in collection)
Element Column (Data)
Non-Index: The Non-Index type collection has a table with 2 columns they are
Key Column
Element Column
Note: Here it won't have any index column because, Since a SET doesn’t retain the insertion order.
Multiplicity
This is a way to implement the HAS-A relation between two entities and the cardinality ratio depends on their relation.
You can use ElementCollection replace for you use #OneToMany. Example you can have one Project in many versions.
#ElementCollection
#CollectionTable(name="versions",
joinColumns = #JoinColumn(name="projectID"))
#LazyCollection(LazyCollectionOption.FALSE)
#JoinColumn(name="version",nullable = false)
private Set<String> versions;
You also can use #ElementCollection in mapping OGM for array in one collection.
#ElementCollection(fetch = FetchType.EAGER)
private Set<String> researchAreas;

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.

How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?

I have the following two annotated classes that I use to build a graph:
#Entity
#Table(name = "Edge")
public class Edge
{
/* some code omitted for brevity */
#ManyToOne
#JoinColumn(name = "ixNodeFrom", nullable = false)
private Node _nodFrom;
#ManyToOne
#JoinColumn(name = "ixNodeTo", nullable = false)
private Node _nodTo;
/* some code omitted for brevity */
}
#Entity
#Table(name = "Node")
public class Node
{
/* some code omitted for brevity */
#OneToMany(mappedBy = "_nodTo")
private Set<Edge> _rgInbound;
#OneToMany(mappedBy = "_nodFrom")
private Set<Edge> _rgOutbound;
/* some code omitted for brevity */
}
Now, when I build the graph, I issue two queries to fetch all rows from either table and set up the child / parent references, for which I need the ids stored in the Edge table.
Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily * loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.
I tried adding these lines to the Edge class, but then my JPA provider wants me to make one mapping read-only, and I can't seem to find a way how to do that:
#Column(name = "ixNodeTo")
private long _ixNodeTo;
#Column(name = "ixNodeFrom")
private long _ixNodeFrom;
I'm using Eclipselink and MySQL, if it matters.
**The default behaviour for #ManyToOne actually is eager loading, see Pascal's answer*
I got three good answers that were equally helpful, and by now none percolated to the top by public vote, so I'm merging them together here for a single comprehensive answer:
a) Change the query
You can load the whole graph at once by changing the query, thereby giving the JPA provider a chance to realize that it already has everything in memory and doesn't need to go back to the DB:
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
(via axtavt)
b) Use read-only fields for the FKs
Loading the FKs into their own fields, as described in the question, will also work if, as the JPA provider is demanding, the fields are declared to be readonly, which is done like this:
#Column(name = "ixNodeTo", insertable = false, updatable = false)
(via bravocharlie)
c) Use property access
If you are using property access instead of field access, the JPA provider also gets a chance to realize it already has the FK and doesn't need to fetch the referenced object. In short, property access means that you put the JPA annotations on the getter, thereby "promising" the JPA provider that your getter won't go and access the rest of the object. More details in this question. This will work for Hibernate, and for Eclipselink, it will work (assumed in the original answer, experimentally confirmed by me) with weaving enabled. (via Pascal Thivent)
Additionally, as Pascal points out in his answer, #ManyToOne, contrary to my original post, is not lazy-loading, but eager-loading by default, and changing that will require weaving as well.
Have you tried
#Column(name = "ixNodeTo", insertable = false, updatable = false)
How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?
In theory, a JPA provider should be able to not trigger a query when calling
someEdge.getNodeFrom().getId()
as it already has the id (as FK).
I'm 100% sure Hibernate can (assuming you're using property access). In the case of EclipseLink, I don't know (if it does, it will probably requires weaving).
Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.
Note that #ManyToOne uses an EAGER strategy by default. If you want to make it LAZY, you have to decalre it explicitly (but again, this will require weaving of your classes with EclipseLink).
I think you should try to optimize your query rather than change the mapping. For example, the following query fetches the whole graph at once (tested in Hibernate):
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
How about using getReference()?
For example:
Node fkNode = em.getReference(edge.getNodeFrom()); // [1]
fkNode.getId()
[1] This will not trigger a SQL query to retrieve the nodeFrom

Categories

Resources