I want to get a specific row in a OneToMany relation. E.g. getting the cheapest item of an order
Example:
public class Order {
#Id
#Column(name = "ORDER_ID")
private Long id;
???
private Item cheapestItem;
}
public class Item {
#Id
#Column(name = "ITEM_ID")
private Long id;
private Long price;
}
How can I do this?
Try specifying a where clause in the hibernate #Where annotation (Not sure if you can apply it to a non-collection, though)
I want to get (...) the cheapest item of an order
If you really want to get the cheapest Item (without actually persisting it), it should be is doable with a ManyToOne and a JoinColumnOrFormula. Requires Hibernate 3.5+, see issues like HHH-4382 and HHH-5041 for examples.
Retrieving only the price would be much easier and doable with previous versions of Hibernate. See Hibernate Derived Properties - Performance and Portability.
Related
I use crnk (JSON-API) in java project and I have 3 questions regarding its usage with spring boot and jpa - haven't found exact implementation details in documentation.
For example, I have 2 entities and respective tables:
#Entity
#JsonApiResource(type = "employee")
public class Employee {
#Id
#JsonApiId
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "typeId")
private EmployeeType employeeType; //stored in table as typeId
}
#Entity
#JsonApiResource(type = "type")
public class EmployeeType {
#Id
#JsonApiId
private int id;
private String typeName;
private int salary;
}
How should JsonApiRelation be introduced in order to be able to call "/employee/1" and "/employee/1/type" urls?
For example there is one more entity.
#Entity
#JsonApiResource(type = "project")
public class Project {
#Id
#JsonApiId
private int id;
private String supervisorName;
private String projectName;
}
First, I'd like to have List of Projects for each Employee, where he is a supervisor, joint by name and have it listed as attribute in Json.
Tried implementing it with #OneToMany and #JoinColumn annotations but got StackOverflowException. How could this be implemented. And second, how could this be implemented with Relation? Like "/employee/1/projects" url.
How should I implement custom filtering of results for findAll method? For example, I have a List of all Employees, but I'd like to exclude some of them from the response. Which class/method should be introduced for this behaviour?
#JsonApiRelation annotation should not be necessary. Crnk will detect the #ManyToOne annotation and map it accordingly.
in case of crnk-jpa it is sufficient to specify all relationships in JPA. Matching JSON API relationships. So your approach seems good. What was the StackoverflowException stacktrace? (next to the examples, there are also many example entities in crnk-jpa)
I would make use of a decorator. See http://www.crnk.io/documentation/#_request_filtering. RepositoryDecoratorFactory allows to place a custom repository between the caller and crnk-jpa (or any other kind of repository). There you can do any kind of modification perform (maybe) calling the "real" repository. => Will add an example for this
feel free also make open up tickets in crnk for any documentation/example clarifications.
We have in our spring boot (1.5.6) app a classical bi-directional OneToMany hierarchy in our entities, e.g. an Order has many Item.
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "order", )
#JsonManagedReference
private Set<Item> items = new HashSet<>();
// getters and setters
}
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JsonBackReference
private Order order = new HashSet<>();
// getters and setters
}
In our JavaScript-view, which uses rest-controllers to fetch the data, sometimes the Order and sometimes the Items is the root object. Let's say we have somekind of "Show Order" and another "Show Item" view.
So, if Order is the root, we want to know also its children (items) and if an Item is the root, we also want to know its parent order.
The serialization obviously would generate an infinite recursion, so we would normally use #JsonManagedReference and #JsonBackReference here.
However, this removes the back-reference (so we have a one-directional serialization here...). We also tried to use JsonIdentityInfo - which does not work, because elasticsearch has some problems during the deserialization and we also have some cross-references which will be also replaced by the ids and messes everything up...
Long story short, our idea is now to have somekind of "depth-break":
When the first serialized object is an Order, then its items-property should be fully serialized. But for each Item the back-reference to order should not be serialized (one can say here "the depth of order is 2").
When the first serialized object is an Item then its back-reference order is serialized, but the second managed-refrence to items will be ignored.
We tried also JsonView for this, but we cannot dynamically set this value.
Anyone who solved that or has an approach what we can use for this?
Instead of #JsonManagedReference and #JsonBackReference you can use #JsonIgnoreProperties annotation to suppress serialization of nested properties:
public class Item {
//...
#JsonIgnoreProperties("items")
#ManyToOne
private Order order = new HashSet<>();
//...
}
I have two model classes: Equity and EquityData. There is a OneToMany relationship from Equity to EquityData. I'm having a hard time getting Hibernate to bind the way I want it to.
#Entity
#Table(name="equities")
public class Equity
{
#Id
#GeneratedValue
#Column(name="Equity_ID")
private Integer id;
private String symbol;
#OneToMany(mappedBy="equity")
private List<EquityData> equityData;
...
}
#Entity
public class EquityData
{
#Id
#GeneratedValue
#Column(name="id")
private Integer id;
#ManyToOne
#JoinColumn(name="Equity_ID")
private Equity equity;
#Column(name="quote_time") private Date quoteTime;
#Column(name="quote_type_id") private Integer quoteTypeId;
#Column(name="value") private BigDecimal value;
...
}
Now an Equity can have many EquityQuotes, but there will always be a "most recent" quote (the one with the latest quoteTime). Right now, the way I have Hibernate bind to my entities, it'll retrieve the Equity and all the EquityData's. I only want it to retrieve the latest EquityData for each EquityDataType (i.e. i dont care about yesterday's data, just today's).
In SQL, it would look like this:
select d.equity_id, d.quote_type_id, d.value, max(quote_time)
from equities e, equity_data d
where e.equityID = d.equity_id and e.symbol = :symbol
group by d.equity_id, d.quote_type_id;
I'd appreciate any help! I don't think it matters, but I'm using this in the Stripes Web Framework.
You can set the fetch type as LAZY on your equity data list and then write a named query and fetch only today's equity data. Since you are using Hibernate, you can achieve this using the Criteria API as well.
You can set a criteria to fetch the latest quote from the database like,
Criteria crit = session.createCriteria(Equity.class);
//your criteria goes here....
crit.createCriteria(last_quoted);
List<?> entity = crit.list();
for(Iterator<?> it = equity.iterator();it.hasNext();){
Equity equity = (Equity) it.next();
//print the latest quotes based on the criteria you provided
}
session.close();
}
//catch(Exception e){ //display exeption;}
You can create another field to recover only the EquityData from today, adding an #Where clause to it:
#OneToMany(mappedBy = "equity")
#Where(clause = "quoteTime >= TODAY")
private List<EquityData> equityDataFromToday;
Then, you must use the field equityDataFromToday to access your data, instead of equityData (which will contain all of them)
I Have two entities CRImageType & CRVariable with a many to many relation as follows:
CRImageType entity:
#Entity
#Table(name = "imageviewer_crimagetype")
public class CRImageType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "ImTypeId")
private Long imTypeId;
#Column(name = "ImTypeName")
private String imTypeName;
#Column(name = "ImTypeDescription")
private String imTypeDescription;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="imageviewer_imtype_variable",
joinColumns={#JoinColumn(name="ImTypeId")},
inverseJoinColumns={#JoinColumn(name="VarId")})
private Set<CRVariable> crvariables = new HashSet<CRVariable>();
CRVariable entity:
#Entity
#Table(name = "imageviewer_crvariable")
public class CRVariable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "VarId")
private Long varId;
#Column(name = "VarName")
private String varName;
#Column(name = "VarDescription")
private String varDescription;
#ManyToMany(mappedBy="crvariables")
private Set<CRImageType> crimagetypes = new HashSet<CRImageType>();
In my database the relation is mapped by two tables "imageviewer_crimagetype" & "imageviewer_crvariable" and a third one "imageviewer_imtype_variable" for their many to many relation.
I would like only to DELETE association records from table "imageviewer_imtype_variable". How can be done using an HQL query since i can not directly access "imageviewer_imtype_variable table.
I would like the HQL equivalent of an SQL query like
delete from imageviewer_imtype_variable where ImTypeId='%%%'
This is JPA, not Hibernate specifically. The fact that you have a standardized API on top here makes it easier to find answers if you search in the context of the API, not the implementation.
The way to do it (as far as I remember, I don't use many to many relationships that often) is to remove the related entities from each other's collection mapping fields. So if you have EntityA and EntityB, you remove EntityA from EntityB and EntityB from EntityA. The persistence provider should then be triggered to remove the record from the join table.
Native queries should only be a last resort IMO.
You can execute a native SQL query:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch18.html#querysql-creating
So in your case, something like:
session.createSQLQuery("DELETE FROM imageviewer_imtype_variable").executeUpdate();
You can also specify a custom native SQL DELETE query within your CrImageType entity:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch18.html#querysql-cud
From my experience, handling the ManyToMany relation is one of the few case where getting out of ORM integrism is the best option for our mind's sake. Particularly, when you use a two way navigation (i.e. when the relation and the inverse relation are usefull).
#Gimby is correct in his answer, the thing is that with a complex cross referencing it is far harder to make it work, than doing a simple native query.
So:
session.createSQLQuery("DELETE FROM imageviewer_imtype_variable").executeUpdate();
is easier, if it still does not work because of cross referencing, you migh even add a :
session.clear();
OK. This is another ORM integrism infringment, but get you out of the cesspit in two lines ;-) .
The answer from #Gimby is the correct one, in a many to many relationship removing related instances from each other mapping collection triggers a delete from the relationship mapping table.
In the entity class CRVariable you add :
#PreRemove
private void removeCRVariableFromCRImageType() {
for (CRImageType crImageType: CRImageType) {
crImageType.getCrvariables ().remove(this);
}
}
This method will override the JPA action PreRemove in order to detach the CRVariable object to be removed from the set crvariables (technically the table imageviewer_imtype_variable)
Hope this helps you !
I need to read a complex model in an ordered way with eclipselink. The order is mandantory because it is a huge database and I want to have an output of a small portion of the database in a jface tableview. Trying to reorder it in the loading/quering thread takes too long and ordering it in the LabelProvider blocks the UI thread too much time, so I thought if Eclipselink could be used that way, that the database will order it, it might give me the performance I need. Unfortunately the object model can not be changed :-(
The model is something like:
#SuppressWarnings("serial")
#Entity
public class Thing implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
#PrivateOwned
private List<Property> properties = new ArrayList<Property>();
...
// getter and setter following here
}
public class Property implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
#OneToOne
private Item item;
private String value;
...
// getter and setter following here
}
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
....
// getter and setter following here
}
// Code end
In the table view the y-axis is more or less created with the query
Query q = em.createQuery("SELECT m FROM Thing m ORDER BY m.name ASC");
using the "name" attribute from the Thing objects as label.
In the table view the x-axis is more or less created with the query
Query q = em.createQuery("SELECT m FROM Item m ORDER BY m.name ASC");
using the "name" attribute from the Item objects as label.
Each cell has the value
Things.getProperties().get[x].getValue()
Unfortunately the list "properties" is not ordered, so the combination of cell value and x-axis column number (x) is not necessarily correct. Therefore I need to order the list "properties" in the same way as I ordered the labeling of the x-axis.
And exactly this is the thing I dont know how it is done. So querying for the Thing objects should return the list "properties" "ORDER BY name ASC" but of the "Item"s objects. My ideas are something like having a query with two JOINs. Joing Things with Property and with Item but somehow I was unable to get it to work yet.
Thank you for your help and your ideas to solve this riddle.
May be the answer to this other question could help you:
Defining the order of a list
I think you may have to use another query to get the list of properties order by item.name for each thing.
Something like:
SELECT p FROM Property p WHERE p.thing = ?1 ORDER BY p.item.name
Try the #OrderBy JPA annotation. Something like
#OrderBy('name ASC')
#OneToMany(cascade=CascadeType.ALL)
private List<Property> properties = new ArrayList<Property>();