Suppose I have a Post entity and a Comment entity and a one to many relationship:
#Entity class Post {
...
#OneToMany
List<Comment> comments;
...
}
How can I achieve paging like this:
Post post = //Find the post.
return post.getComments().fetch(100, 10); // Find the 11th page (page size 10);
Is it possible to emulate dynamic paging with #OneToMany collections on top of JPA,
or do we have to rewrite the association mechanism of JPA totally ? (e.g. create a PersistentList collection type that could manage the paging, sorting and searching).
P.S.: I recently found the Play! framework uses a very interesting lib on top of JPA: Siena. Siena is very easy to use, and is a good abstraction on top of JPA/Hibernate. But I can't find how to do paging with its associations.
Update:
Play framework has a query syntax similar to Django:
Post.findAll().from(100).fetch(10); // paging
where
Post.findAll()
will return a JPAQuery object, a customized query type in Play.
But with associated collections, e.g.:
Post.comments
will just return a List, which doesn't support paging or other queries.
I was wondering how to extend it, so that
Post.comments
will also return a JPAQuery object or similar, then you can query on the "query" collection:
Post.comments.from(100).fetch(10);
or insert a new Comment without actually fetching any of the comments:
Post.comments.add(new Comment(...));
On my first thought, we could create a subclass of List, then the Post class would become:
#Entity class Post {
...
#OneToMany
QueryList<Comment> comments;
...
}
and QueryList will have fetch(), from() methods that indirect to JPAQuery's.
But I don't know whether Hibernate/JPA will recognize this, or interfere with it.
Is it possible to emulate dynamic paging with #OneToMany collections on top of JPA (...)
Not supported. The standard approach would be to use a JPQL query to retrieve the comments for a given post and and to use Query#setFirstResult(int) and Query#setMaxResults(int).
On my first thought, we could create a subclass of List, (...). But I don't know whether Hibernate/JPA will recognize this, or interfere with it.
It obviously won't without an heavy patch to drastically change the default behavior.
I think the "right" way might be more like:
#Entity
class Post {
...
public GenericModel.JPAQuery getComments() {
return Comment.find("post_id = ?", post_id);
}
}
and then use one of the fetch methods in JPAQuery:
// fetch first page of results, 25 results per page
post.getComments().fetch(1,25);
Related
In my application, a non-standard situation, I have a layer of entity in mysql, layer of dominain in controllers. My domain model contains a few entities, can this be integrated into one JPQL query?
entity layer:
PersonEntity table
EventEntity table
EventVisitorEntity table
PersonEntity many to many EventEntity
EventVisitorEntity interim table
domain layer:
class PersonInfo {
Person person;
List<PersonEvent> personEvent
...
}
Now I get all Person to take their ids and get the PersonEvent, using this query:
#Query("SELECT new domain.PersonEvent(ev.personId,ev.eventId,e.name,ev.state)" +
" FROM EventVisitorEntity AS ev ,EventEntity AS e WHERE e.id = ev.eventId AND ev.personId IN (?1)")
List<PersonEvent> findEventsForPerson(List<Integer> ids);
It is possible to write one query to get persons with an personEvents ?
in the constructor which is below:
public PersonInfo(Person person, List<PersonEvent> personEvents)
Hardly doubt it is possible. JPQL subqueries, which might help you outline personEvents, are allowed only in where and having clauses.
Instead, I'd suggest you to just embrace the query as-is and move the logic of gathering to your DAO tier. This link might be helpful: https://dzone.com/articles/add-custom-functionality-to-a-spring-data-reposito. Declare a method List<PersonEvent> findEventsForPerson(List<Integer> ids), implement custom repository for it, doing all nesessary JPQL queries and combinations there. But beware of N+1 issue.
Also it may be convenient to use entity graphs in such custom implementation.
EDIT: After rereading the spec on fresh mind, I realized that I have mistaken saying that subqueries are allowed only in WHERE/HAVING clauses. It says that it may be used there, which doesn't exclude the opposite. Anyway, even if it is possible, such approach (extracting relation via subqueries) would most probably lead to N+1 issues, unless JPA implementors are smart enough to predict that (I wouldn't count on that anyway).
I have an entity holding a collection (#OneToMany) which loads lazily. So far so good. If I load the entire list of entity objects (findAll()) I don't want the collection loaded at all. I don't access the collection therefore I assumed it will not be loaded before returning it from a REST endpoint, but it seems like Jackson accesses it when parsing it into JSON.
Currently I iterate over the entire entity list and set the collection to NULL. This seems like a very poor way of doing it, is there a way to ONLY manually load the collection with a specially prepared #Query and not load it automatically (either LAZY no EAGER) at all? Are #JsonViews the correct way to go or should I remove the #OneToMany annotation (I guess then I lose the mapping for the queries that actually do load the collection)? Any other suggestions?
Examplecode
#Entity
#Entity
public class Entity {
#OneToMany(targetEntity = Child.class)
private List<Child> children;
}
Jersey Resource
#GET
#Produces({MediaType.APPLICATION_JSON})
public List<Entity> getAllEntities() {
List<Entity> entities = entityService.findAll();
entities.forEach(e-> e.setChildren(null));
return entities ;
}
Repository = JpaRepository with default findAll() implementation.
thanks
Since you mentioned 'suggestion', I faced the same problem myself and I decided to implement custom DTOs to be sent in the API response. So I ommitted these collection fields and all other I did not want the json processors to touch.
I did implement my set of DTOs mirroring actual persisted entities, but there might be some other mappers to do the job
A few time ago, I asked a question about designing model classes for a REST API. There might be some information there useful for you.
Instead of reusing the same model classes for persistence and for the REST API, I've realized the best approach was creating different models. In some situations you don't want the persistence model to be the same as the model you use in your API. So, defining different models is the way to go.
And I chose MapStruct to map from one model to other.
I have an entity with the following field:
#ManyToMany(cascade = { CascadeType.ALL }, targetEntity = Comment.class)
#JoinTable(name = "program_to_comment")
#OrderBy("position")
private Set<Comment> comments = new HashSet<Comment>();
but I have the problem that whenever I persist it using:
Program p = entityManager.persist(entity);
the field comes with the objects sorted as it was sorted in the entity object.
Suppose the entity object is configured as following: Program(comments:[Comment(position:15), Comment(position:10)], ...), persisting the entity (entityManager.persist), it will store both comments and the program entity itself to the database. But the resulted entity from the persist method invocation is an object as follows: Program(comments:[Comment(position:15), Comment(position:10)], ...), in the same order gave to the persist method.
From my point of view at this point the resulted entity should present the values following the specified #OrderBy rule, or am I missing something?
Additional information:
JPA2
Hibernate 4.2.0.Final
OrderBy simply add an order by clause to the query used to load the comments of a program. Nothing more. The rest is under your responsibility. So if you want the comments sorted by position when adding comments and persisting them, you have to take care of this by yourself.
I have personally never found this annotation to be really useful. I have also found it not to work in every case, particularly when using a query to fetch programs with their comments, with an order by clause already present in the query. I generally prefer not to use theis annotation, and provide a getSortedComments() method which returns a sorted set or list of comments, using a comparator.
I need to allow client users to extend the data contained by a JPA entity at runtime. In other words I need to add a virtual column to the entity table at runtime. This virtual column will only be applicable to certain data rows and there could possibly be quite a few of these virtual columns. As such I don't want to create an actual additional column in the database, but rather I want to make use of additional entities that represent these virtual columns.
As an example, consider the following situation. I have a Company entity which has a field labelled Owner, which contains a reference to the Owner of the Company. At runtime a client user decides that all Companies that belong to a specific Owner should have the extra field labelled ContactDetails.
My preliminary design uses two additional entities to accomplish this. The first basically represents the virtual column and contains information such as the field name and type of value expected. The other represents the actual data and connects an entity row to a virtual column. For example, the first entity might contain the data "ContactDetails" while the second entity contains say "555-5555."
Is this the right way to go about doing this? Is there a better alternative? Also, what would be the easiest way to automatically load this data when the original entity is loaded? I want my DAO call to return the entity together with its extensions.
EDIT: I changed the example from a field labelled Type which could be a Partner or a Customer to the present version as it was confusing.
Perhaps a simpler alternative could be to add a CLOB column to each Company and store the extensions as an XML. There is a different set of tradeoffs here compared to your solution but as long as the extra data doesn't need to be SQL accessible (no indexes, fkeys and so on) it will probably be simple than what you do now.
It also means that if you have some fancy logic regarding the extra data you would need to implement it differently. For example if you need a list of all possible extension types you would have to maintain it separately. Or if you need searching capabilities (find customer by phone number) you will require lucene or similar solution.
I can elaborate more if you are interested.
EDIT:
To enable searching you would want something like lucene which is a great engine for doing free text search on arbitrary data. There is also hibernate-search which integrates lucene directly with hibernate using annotations and such - I haven't used it but I heard good things about it.
For fetching/writing/accessing data you are basically dealing with XML so any XML technique should apply. The best approach really depends on the actual content and how it is going to be used. I would suggest looking into XPath for data access, and maybe look into defining your own hibernate usertype so that all the access is encapsulated into a class and not just plain String.
I've run into more problems than I hoped I would and as such I decided to dumb down the requirements for my first iteration. I'm currently trying to allow such Extensions only on the entire Company entity, in other words, I'm dropping the whole Owner requirement. So the problem could be rephrased as "How can I add virtual columns (entries in another entity that act like an additional column) to an entity at runtime?"
My current implementation is as follow (irrelevant parts filtered out):
#Entity
class Company {
// The set of Extension definitions, for example "Location"
#Transient
public Set<Extension> getExtensions { .. }
// The actual entry, for example "Atlanta"
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "companyId")
public Set<ExtensionEntry> getExtensionEntries { .. }
}
#Entity
class Extension {
public String getLabel() { .. }
public ValueType getValueType() { .. } // String, Boolean, Date, etc.
}
#Entity
class ExtensionEntry {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "extensionId")
public Extension getExtension() { .. }
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "companyId", insertable = false, updatable = false)
public Company getCompany() { .. }
public String getValueAsString() { .. }
}
The implementation as is allows me to load a Company entity and Hibernate will ensure that all its ExtensionEntries are also loaded and that I can access the Extensions corresponding to those ExtensionEntries. In other words, if I wanted to, for example, display this additional information on a web page, I could access all of the required information as follow:
Company company = findCompany();
for (ExtensionEntry extensionEntry : company.getExtensionEntries()) {
String label = extensionEntry.getExtension().getLabel();
String value = extensionEntry.getValueAsString();
}
There are a number of problems with this, however. Firstly, when using FetchType.EAGER with an #OneToMany, Hibernate uses an outer join and as such will return duplicate Companies (one for each ExtensionEntry). This can be solved by using Criteria.DISTINCT_ROOT_ENTITY, but that in turn will cause errors in my pagination and as such is an unacceptable answer. The alternative is to change the FetchType to LAZY, but that means that I will always "manually" have to load ExtensionEntries. As far as I understand, if, for example, I loaded a List of 100 Companies, I'd have to loop over and query each of those, generating a 100 SQL statements which isn't acceptable performance-wise.
The other problem which I have is that ideally I'd like to load all the Extensions whenever a Company is loaded. With that I mean that I'd like that #Transient getter named getExtensions() to return all the Extensions for any Company. The problem here is that there is no foreign key relation between Company and Extension, as Extension isn't applicable to any single Company instance, but rather to all of them. Currently I can get past that with code like I present below, but this will not work when accessing referenced entities (if for example I have an entity Employee which has a reference to Company, the Company which I retrieve through employee.getCompany() won't have the Extensions loaded):
List<Company> companies = findAllCompanies();
List<Extension> extensions = findAllExtensions();
for (Company company : companies) {
// Extensions are the same for all Companies, but I need them client side
company.setExtensions(extensions);
}
So that's were I'm at currently, and I have no idea how to proceed in order to get past these problems. I'm thinking that my entire design might be flawed, but I'm unsure of how else to try and approach it.
Any and all ideas and suggestions are welcome!
The example with Company, Partner, and Customer is actually good application for polymorphism which is supported by means of inheritance with JPA: you will have one the following 3 strategies to choose from: single table, table per class, and joined. Your description sounds more like joined strategy but not necessarily.
You may also consider just one-to-one( or zero) relationship instead. Then you will need to have such relationship for each value of your virtual column since its values represent different entities. Hence, you'll have a relationship with Partner entity and another relationship with Customer entity and either, both or none can be null.
Use pattern decorator and hide your entity inside decoratorClass bye
Using EAV pattern is IMHO bad choice, because of performance problems and problems with reporting (many joins). Digging for solution I've found something else here: http://www.infoq.com/articles/hibernate-custom-fields
I'd like to use Hibernate's Criteria API for precisely what everybody says is probably its most likely use case, applying complex search criteria. Problem is, the table that I want to query against is not composed entirely of primitive values, but partially from other objects, and I need to query against those object's id's.
I found this article from 2 years ago that suggests it's not possible. Here's how I tried it to no avail, there are other aspect of Hibernate where I know of where this sort of dot notation is supported within string literals to indicate object nesting.
if (!lookupBean.getCompanyInput().equals("")) {
criteria.add(Restrictions.like("company.company", lookupBean.getCompanyInput() + "%"));
}
EDIT:
Here's my correctly factored code for accomplishing what I was trying above, using the suggestion from the first answer below; note that I am even using an additional createCriteria call to order on an attribute in yet another associated object/table:
if (!lookupBean.getCompanyValue().equals("")) {
criteria.createCriteria("company").add(
Restrictions.like("company", lookupBean.getCompanyValue() + "%"));
}
List<TrailerDetail> tdList =
criteria.createCriteria("location").addOrder(Order.asc("location")).list();
Not entirely sure I follow your example, but it's certainly possible to specify filter conditions on an associated entity, simply by nesting Criteria objects to form a tree. For example, if I have an entity called Order with a many-to-one relationship to a User entity, I can find all orders for a user named Fred with a query like this:
List<Order> orders = session.createCriteria(Order.class)
.createCriteria("user")
.add(eq("name", "fred"))
.list();
If you're talking about an entity that has a relationship to itself, that should work as well. You can also replace "name" with "id" if you need to filter on the ID of an associated object.