JPQL #ManyToMany querying - mysql

I need to write two different yet similar JPQL queries and currently lack the expertise. I really hope one can help.
Return all items associated with one or more tags, by querying the current Tag class and returning the collection of items?
Return all items that holds a reference to the current Tag class, by querying over every item and comparing with the collection of tags?
Additional questions:
When i add a tag to the collection of an item, do the item automatically get added to the collection in the tag?
Any ideas on how i can sort the returned items depending on the number of tags they match? Can i include this in the JPQL query?
Would it be better for my Tag class to have the String keyword as #id?
My code:
#Entity
#NamedQueries({
#NamedQuery(name = Item.FIND_ALL, query = "select i from Item i")
})
#TableGenerator(name = "Item_ID_Generator", table = "ITEM_ID_GEN", pkColumnName = "PRIMARY_KEY_NAME",
pkColumnValue = "Item.id", valueColumnName = "NEXT_ID_VALUE")
public class Item implements Serializable {
public static final String FIND_ALL = "Item.findAll";
// private static final long serialVersionUID = 1L;
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.TABLE, generator = "Item_ID_Generator")
private Integer id;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
#JoinTable(name = "jnd_item_tag",
joinColumns = #JoinColumn(name = "item_fk"),
inverseJoinColumns = #JoinColumn(name = "tag_fk"))
private List<Tag> tags = new ArrayList();
}
#Entity
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(unique = true)
private String keyword;
#ManyToMany(mappedBy = "tags")
private List<Item> referencedByItem;
}

Execute the following query, which will return the matched tags sets, and the matched tags:
SELECT i, t FROM Item i JOIN i.tags t WHERE t.keyword IN :listOfTags
I this case can be this way:
#NamedQuery(name = "selectByTags", query = "SELECT i, t FROM Item i JOIN i.tags t WHERE t.keyword IN :listOfTags")
//and here you need set your list
List<Tag> tags = daoTags.getListTags();
query.setParameter("listOfTags", listOfTags);
I think that adapting this query solves both requests for your question.

Related

Getting parent_id for nested children

I'm new to JPA and I'm stumped on how to capture the parent id param for nested children. I have two entities, Data and Property. Right now I successfully managed to create a one to many mapping from Data to Property. The thing is that Property can have its own children of itself. The "first layer" properties are able to get and save the data id value, but properties nested in another property are unable to do so. I was wondering if there is a method to do something like this since I could not find any information on it online.
Data (parent class)
#Entity
#Table(name = "data")
public class Data implements Serializable {
// omitted non relevant code
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#OneToMany(targetEntity = EventProperty.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "data_id", referencedColumnName = "id")
private List<EventProperty> properties;
}
Property (child class)
#Entity
#Table(name = "property")
public class Property implements Serializable {
private static final long serialVersionUID = -1100374255977700675L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#ManyToOne
#JoinColumn(name = "data_id", referencedColumnName = "id")
private EventData eventData;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private EventProperty parent;
#OneToMany(targetEntity = EventProperty.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private List<EventProperty> children;
}
I'm wondering if storing the properties as a List in Data is the right way to go, or if there's any way to set data_id of a child Property to be the same as the parent Property if it is null, or if there's some other correct way that I am not aware of. Any help would be greatly appreciated!

Jackson JSON infinite recursion without omiting anything from the serialization

I want to serialize and eventually deserialize an object to perform an export/import operation. I use Jackson library because of the extend annotation provided. I do break the infinite recursion by using the latest tags #JsonManagedReference, #JsonBackReference. But the problem here #JsonBackReference does omit the annotated part from the json file so I am not able to set the relationship while importing.
The relationship btwn entities can be shown:
public class A{
#Id
#Column(name = "ID", unique = true, precision = 20)
#SequenceGenerator(name = "a_generator", sequenceName =
"SEQ_A", initialValue = 1, allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator =
"a_generator")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "metricDefinition",
fetch = FetchType.LAZY, orphanRemoval = true)
#Fetch(FetchMode.SELECT)
#NotAudited
#JsonManagedReference
private Set<B> bSet= new HashSet<B>();
}
public class B{
#Id
#Column(name = "id", unique = true, precision = 20)
#SequenceGenerator(name = "b_generator", sequenceName = "seq_b", initialValue = 1, allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "b_generator")
private Long id;
#ManyToOne(cascade = {CascadeType.REFRESH})
#JoinColumn(name = "a_id")
#Fetch(FetchMode.SELECT)
#JsonBackReference(value = "a-b")
private A a;
#ManyToOne(cascade = {CascadeType.REFRESH})
#JoinColumn(name = "ref_a_id")
#Fetch(FetchMode.SELECT)
#JsonBackReference(value = "a-ref")
private A refA;
#Column(name = "is_optional")
#Fetch(FetchMode.SELECT)
private boolean isOptional;
#Column(name = "name")
#Fetch(FetchMode.SELECT)
private String name;
When I serialize any A object, it does serialize the B's included but the referenced A and refA are omitted. So, when I import A object of course the B's are also imported but I do want to the relationship between the objects to be set.
Is there any idea how can I break the infinite recursion without omitting the one side of the reference?
Thanks in advance
I did also try to use statement below according to answers given similar questions but it did not work so I asked the question above.
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "#id")
it does not sufficient to break the circle. You should also add below annotation to the id property of your class.
#JsonProperty("id")
We can try to break the loop either at the Parent end or at the Child end by following 3 ways
Use #JsonManagedReference and #JsonBackReference
Use #JsonIdentityInfo
Use #JsonIgnore
Use #JsonIdentityInfo
#Entity
#Table(name = "nodes")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Node {
...
}
#Entity
#Table(name = "relations")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Relation {
...
}
Refer more in detail here with the working demo at the end.

HQL Many to Many JOIN with extra columns

I have a many to many relationship in Hibernate with additional colums so I have an extra java class for the model and another class with the primary key... now in HQL I need a query that retrieve those data but I have problems with Join condition.
This is my First class:
#Entity
#Table(name = "Firsts")
public class First {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToMany(mappedBy = "primaryKey.second")
private List<FirstsSeconds> seconds = new LinkedList<>();
#Column(name="description")
private String description;
...
}
The Second class:
#Entity
#Table(name="Seconds")
public class Second {
#Id
private String code;
#OneToMany(mappedBy = "primaryKey.first")
private List<FirtsSeconds> firsts = new LinkedList<>();
...
}
And the table manyToMany with additional columns:
#Entity
#Table(name = "firsts_seconds")
#AssociationOverrides({ #AssociationOverride(name = "primaryKey.first", joinColumns = #JoinColumn(name = "id")),
#AssociationOverride(name = "primaryKey.second", joinColumns = #JoinColumn(name = "code")) })
public class FirstsSeconds{
#EmbeddedId
private FirstsSecondsId primaryKey = new FirstsSecondsId();
#Column(name = "extra", nullable = false)
private String extra;
...
}
So the id class:
#Embeddable
public class FirstsSecondsId {
#ManyToOne
private First first;
#ManyToOne
private Second second;
...
}
Finally to get HQL result I create a new class with the field that I want:
public class NewObject
public CargoOrder(String firstDescription, String fsExtra) {
this.firstDescription = firstDescription;
this.fsExtra = fsExtra;
}
...
First of all I want First descrption and extra from FirstsSecond, so this is my query with JOIN fr.seconds as fs:
#Query("SELECT new com.mypackage.NewObject("
+ "fr.description as firstDescription, fs.extra as fsExtra) "
+ "FROM First as fr"
+ "JOIN fr.seconds as fs")
public List<NewObject> findManyToMany();
But I have no results :(... in this case I have to specify the where condition?
#Query("SELECT new com.mypackage.NewObject("
+ "fr.description as firstDescription, fs.extra as fsExtra) "
+ "FROM First as fr"
+ "JOIN fr.seconds as fs WHERE fr.first = fs.primaryKey.first")
public List<NewObject> findManyToMany();
Thats not compile on JOIN fr.seconds as fs WHERE fr.first = fs.primaryKey.first...
Kind regards.
Solved... debugging with spring.jpa.show_sql = true in application-properties I see that there was a bad matching in the join condition, I mapped wrong keys:
In First class it's primaryKey.first (not second):
#OneToMany(mappedBy = "primaryKey.first")
private List<FirstsSeconds> seconds = new LinkedList<>();
And in Second primaryKey.second (not first):
#OneToMany(mappedBy = "primaryKey.second")
private List<FirstsSeconds> firsts = new LinkedList<>();

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column in 'field list' using Criteria and mapping annotation

i have criterias to access result by
First Hibernate Dao is
AnswerText answersText = questionManager.getAnswerTextByAnswerIdAndLanguageId(answers.getAnswerId(), 1L);
#Override
public AnswerText getAnswerTextByAnswerIdAndLanguageId(Number answerId,Number languageId) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(AnswerText.class);
criteria.add(Restrictions.eq("answer.answerId", answerId));
criteria.add(Restrictions.eq("languageId", languageId));
List<AnswerText> results = criteria.list();
return (results !=null && !results.isEmpty()? results.get(0): null);
}
Answers.java
#Entity
#Table(name = "ANSWERS")
#Cacheable
#JsonIgnoreProperties(ignoreUnknown = true)
public class Answer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ANSWER_ID")
private Long answerId;
#Column(name = "QUESTION_ID")
private Long questionId;
#Column(name = "DATE_CREATED")
private Timestamp dateCreated;
#Column(name = "CREATED_BY_ID")
private Long creatorId;
#Column(name = "DATE_MODIFIED")
private Timestamp dateModified;
#Column(name = "MODIFIED_BY_ID")
private Long modifierId;
#OneToMany(fetch = FetchType.EAGER,mappedBy = "answer" )
private Set<AnswerText> answerText = new HashSet<AnswerText>();
//getters and setters
AnswerText.java
#Entity
#Table(name = "ANSWERTEXT")
#Cacheable
#JsonIgnoreProperties(ignoreUnknown = true)
public class AnswerText {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ANSWER_TEXT_ID")
private Long answerTextId;
#ManyToOne
#JoinColumn(name="answerId", insertable=false, updatable=false,
nullable=false)
private Answer answer;
#Column(name = "ANSWER_TEXT")
private String answerText;
#Column(name = "LANGUAGE_ID")
private Long languageId;
//getters and setters
When i access the to fetch resultset, it shows below error
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'answertext2_.answerId' in 'field list'
Then i changed to below in
AnswerText.java
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ANSWER_ID", nullable = false)
private Answer answer;
Answers.java
#OneToMany(fetch = FetchType.EAGER,mappedBy = "answer" )
#Fetch(FetchMode.JOIN)
private Set<AnswerText> answerText = new HashSet<AnswerText>();
This produce no error But fetch results twice on calling
Second HibernateDao call is
List<Answer> answerList = questionManager.getAnswersByQuestionId(Long.parseLong("2"));
System.out.println("answerList :"+answerList1.size());
#Override
public ArrayList<Answer> getAnswersByQuestionId(Number questionId) {
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Answer.class);
criteria.add(Restrictions.eq("questionId", questionId));
ArrayList<Answer> answerList = (ArrayList) criteria.list();
return answerList;
}
Can Please anyone point me what is going wrong here. PLease help me.
You have done right changing the JoinColumn-name.
Your multiple results in my opinion is bound to:
FetchType.EAGER and the selection of
FetchMode JOIN As indicated you’ll have to worry about duplicated
results. On the other hand JOIN creates the least amount of queries.
In a high latency environment a single JOIN could be considerable
faster then multiple SELECTS. Keep in mind that joining too much data
could put a strain on the database.
from http://www.solidsyntax.be/2013/10/17/fetching-collections-hibernate/
With FetchMode.SELECT you would get the result like you want to have it.

Spring Data JPA aggregation group by period of time

at the moment I develop a small eCommerce App with Spring Boot and AngularJS. For my data-access layer I use Spring Data JPA with a MySQL-DB. My next step is, that I want to plot some statistics for specific products. For example: how did the review-ratings from a specific product develop over time. So I would like to specify a period of time (e.g. 01.2016 - 03.2016) and than formulate one query that returns 10 or 15 Points in time (within the range) with the average rating of the reviews in this period. So that i can plot a Chart from that.
I found ways with Spring Data to get the average for one period (Between x and y), but then I would have to make 10 queries to the database (for each dot). So I want to know, if it is possible to formulate a query with spring data that splits a time-period in a fixed number of sub-periods and gets the average of customer-ratings within each sub-period? If yes, how can I achieve that?
An excerpt of my Data Model Looks as follows:
#Entity
#Table(name = "product_placement")
public class ProductPlacement implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_placement_id")
private long id;
#Column(name = "title")
private String title;
...
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="productPlacements")
private Set<CustomerReview> customerReviews;
}
#Entity
#Table(name = "customer_review")
public class CustomerReview implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "customer_review_id")
private String reviewIdentifier;
...
#Column(name = "rating")
private Integer rating;
#Column(name = "dateOfCreation", nullable=true, unique=false)
private LocalDateTime dateOfCreation;
#JsonBackReference
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "review_to_product",
joinColumns = #JoinColumn(name = "customer_review_id"),
inverseJoinColumns = #JoinColumn(name = "product_placement_id")
)
private Set<ProductPlacement> productPlacements;
}
Thank you!