Hibernate query. Cast classes - java

Have hql query
List<Developer> developers = session.createQuery("from Developer d " +
"left join ProjectDeveloper pd on pd.developer.id = d.id " +
"where pd.project.id = " + project.getId()).getResultList();
it work and get a list of the objects, but not a Developer.
Actually, the object with i get looks like a list of arrays, which includes object arrays, which include developer class objects and ProjectDeveloper (mtm class). Unfortunately I can not place a link with the image, but Schema look like:
developers = {ArrayList#4889} size = 4
\/ 0 = {object[2]4897} //what does this do in ArrayList<**Developer**>>??
-> 0 = {Developer#4901} //This is exactly the class I expect in ArrayList
-> 1 = {ProjectDeveloper}//?????
-> 1 = {object[2]4898}
Developer class:
#Table(name = "developers")
#Entity
public class Developer implements GenerallyTable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "first_name")
private String name;
#Column(name = "age")
private int age;
#Column(name = "sex")
private boolean sex;
#Column(name = "salary")
private BigDecimal salary;
//...getters + setters
}
How it impossible (how Developer.class transformed in array with unknown structure), and how do I can get exactly ArrayList of Developers?

Try with createQuery with the type defined https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/Session.html#createQuery-java.lang.String-java.lang.Class-
List<Developer> developers = session.createQuery("select d from Developer d " +
"left join ProjectDeveloper pd on pd.developer.id = d.id " +
"where pd.project.id = " + project.getId(), Developer.class)
.getResultList();
Also you should use setParameter methods
List<Developer> developers = session.createQuery("select d from Developer d " +
"left join ProjectDeveloper pd on pd.developer.id = d.id " +
"where pd.project.id = :proj_id", Developer.class)
.setParameter("proj_id", project.getId())
.getResultList();

Related

How to query a list of users that share N number of records in a join table with one user

I have an entity (user) that has a List of tracks. I'd like to query all users that share N number of tracks with one user using his id. I'm using Spring Data JPA for my data layer.
I already have a native query that is doing what I want but it's returning a List of ids for the users. I would like to get List instead.
This is my user entity:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Table(name = "user")
public class User {
#Id
private String id;
private String email;
private String country;
#ElementCollection
#CollectionTable(name = "user_top_tracks",
joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "track_id")
#JsonIgnore
private List<String> tracks;
#ElementCollection
#CollectionTable(name = "user_top_artists",
joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "artist_id")
#JsonIgnore
private List<String> artists;
#ElementCollection
#CollectionTable(name = "matches",
joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "match")
#JsonIgnore
private List<String> matches;
#JsonIgnore
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Token token;
}
The query I have right now (returning List):
#Query(value = "SELECT that.user_id " +
"FROM user_top_tracks AS this INNER JOIN user_top_tracks " +
"AS that ON that.user_id <> this.user_id " +
"AND that.track_id = this.track_id WHERE this.user_id = ?1 " +
"GROUP BY that.user_id HAVING COUNT(*) >= ?2", nativeQuery = true)
List<String> getMatches(String userId, int matchingTracks);
Database schema
Right now I wrote a method that goes over the return list of ids, fetches them and then adds them to a List of users. It's working fine but I'd like to just make it return a List of users instead.
You just need to add one more join
#Query(value = "SELECT usr.* " +
"FROM user_top_tracks AS this INNER JOIN user_top_tracks " +
"AS that ON that.user_id <> this.user_id " +
"AND that.track_id = this.track_id "+
"JOIN user as usr on that.user_id = usr.id"
"WHERE this.user_id = ?1 " +
"GROUP BY that.user_id HAVING COUNT(*) >= ?2", User.class )
List<User> getMatches(String userId, int matchingTracks);
But I think you can also use JPQL
I ended up doing this and it's working fine.
If i find any better solutions i'll update it.
#Query(value ="SELECT * from user where id IN ("+
"SELECT that.user_id " +
"FROM user_top_tracks AS this INNER JOIN user_top_tracks " +
"AS that ON that.user_id <> this.user_id " +
"AND that.track_id = this.track_id WHERE this.user_id = ?1 " +
"GROUP BY that.user_id HAVING COUNT(*) >= ?2 )", nativeQuery = true)
List<User> getMatches(String userId, int matchingTracks);

Problem with querying entities with a set of entities in Spring Boot #Query

I want to have a Spring Boot #Query in JpaRepository which returns me entities with a set of other entities. I know that I can use findAll() method, and then take only those rows that I'm interested in, but I think that this is much slower. The problem is that my entity Booster contains a Set of other entities Boost, and when i try to query it, I get an error. So, this is my Query:
#Query( "select new app.model.Booster(" +
" B.id, " +
" B.active, " +
" B.games, " +
" B.ownerAccount, " +
" B.boosts, " +
" B.boosterNickname, " +
" B.additionalInformation) " +
"from " +
" Booster B " +
" left join Boost Bo on B.id = Bo.boostingAccount.id " +
"where " +
" B.games like %?1% " +
" and Bo.finished = true " +
"group by " +
" B.id")
List<Booster> findBoostersForOverview(String game);
These are my entity classes:
#Data
#Entity(name = "Booster")
public class Booster {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private boolean active;
private String games;
#OneToOne(mappedBy = "boosterAccount", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Account ownerAccount;
#OneToMany(mappedBy = "boostingAccount", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Boost> boosts;
private String boosterNickname;
private String additionalInformation;
#Data
#Entity(name = "Boost")
public class Boost {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id",nullable=false,unique=true)
private long id;
private Date dateCreated;
private Date dateFinished;
private Date dateLastModification;
private boolean finished;
private boolean paused;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "boosted_account_id", referencedColumnName = "id")
private Account boostedAccount;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "boosting_account_id", referencedColumnName = "id")
private Booster boostingAccount;
#OneToMany(mappedBy = "boost", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Game> games;
private String game;
private String additionalInformation;
private String server;
This is what console shows:
Hibernate:
select
booster0_.id as col_0_0_,
booster0_.active as col_1_0_,
booster0_.games as col_2_0_,
booster0_.id as col_3_0_,
. as col_4_0_,
booster0_.booster_nickname as col_5_0_,
booster0_.additional_information as col_6_0_
from
booster booster0_
//HERE IS SOME MORE LOGS THAT ARE UNNECESSARY HERE
And this is error i get:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '. as col_4_0_, booster0_.booster_nickname as col_5_0_, booster0_.additional_info' at line 1
I know that I can query without Set of Boost, and then in another query get specific Boosts, but in my app there will be a lot of SQL and my code will turn to spaghetti. Is there a simple way to solve my problem?
Can you add the following to your repository without any #Query annotation and see?
List<Booster> findByGamesLikeAndBoostsFinishedIsTrue(String game);
The problem here, you are using GROUP BY. When you are using Group By you have use aggregate functions (COUNT, MAX, MIN, SUM, AVG) to group the result-set by one or more columns in the select column. Aggregate functions are mandatory when using GROUP BY function.
In your case, I hope Group By is not required. Modify your code like below.
#Query( "select new app.model.Booster(" +
" B.id, " +
" B.active, " +
" B.games, " +
" B.ownerAccount, " +
" B.boosts, " +
" B.boosterNickname, " +
" B.additionalInformation) " +
"from " +
" Booster B " +
" left join Boost Bo on B.id = Bo.boostingAccount.id " +
"where " +
" B.games like %?1% " +
" and Bo.finished = true")
List<Booster> findBoostersForOverview(String game);

How to fetch object via spring data(jpql) that is in relation via next other object

I'm using spring data with JPQL grouping select, everything works and stats are counted, but when I want to acces to competition object I'm getting this error:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
I have these entities that I want to collect for stats
#Entity
public class BetCourse {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "MATCH_ID", nullable = false)
private BetMatch betMatch;
}
#Entity
public class BetMatch {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "betMatch")
private List<BetCourse> courses = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COMPETITION_ID", nullable = false)
private Competition competition;
}
#Entity
public class Competition {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "competition")
private List<BetMatch> betMatches = new ArrayList<>();
#OneToMany(fetch = FetchType.LAZY, mappedBy = "competition")
private List<Stats> stats = new ArrayList<>();
}
And I'm digging data from these classes to the Stats class
#Entity
public class Stats {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COMPETITION_ID", nullable = false)
private Competition competition;
}
via this select:
#Query("select new Stats(" +
"c.course, " +
"c.courseType, " +
"count(*), " +
"SUM(CASE WHEN win = true THEN 1 ELSE 0 END), " +
"c.betMatch.competition) " +
"from BetCourse c " +
group by c.course, c.betMatch.competition, c.courseType")
public List<Stats> computeStatsByBetShop();
and when I acces to competiton object
stat.getCompetition()
How can I fetch this object? Is it possible to somehow combine with join fetch?
After creating some code spike, I came with a solution that is pretty easy and straight to follow. Instead of starting your JPQL from the BetCourse table, and do the joins from there, you should start from the Competition table, and then do the inner joins until BetCourse.
This is because from the point of view of BetCourse, the entity is a ManyToOne, and them the JPA somehow get lost. Just doing in the inverse order solve your issue.
#Query("select new Stats(" +
"c.course, " +
"c.courseType, " +
"count(c), " +
"SUM(CASE WHEN win = true THEN 1 ELSE 0 END), " +
"cpt) " +
"from Competition cpt " +
"inner join cpt.betMatches bm " +
"inner join bm.courses c " +
"group by c.course, c.courseType, cpt")
#ReadOnlyProperty
public List<Stats> computeStatsByBetShop();
And here goes a GitHub minimum code following your classes, so you can use it as example, in case it still does not work for you.
Cheers, Nikolas

Filtering the Products of my Caddie with JPQL

My entity Caddiecontains a #OneToMany relation with its Product
#Entity
public class Caddie {
#Id
#GeneratedValue
int id;
#OneToMany
#OrderBy("price")
List<Product> products = new ArrayList<>();
}
#Entity
public class Product{
#Id
#GeneratedValue
int id;
#Column(nullable=false)
String name;
}
I want all products from my caddie that are more expensive than 2$.
I have tried many things including this one :
String query = " SELECT p FROM Product p "
+ "JOIN Caddie c "
+ "WHERE c.id = 1 "
+ "AND p MEMBER OF c.products AND p.price > :price";
Unfortunately, I have two Path there. Trying :
String query = " SELECT c.products FROM Caddie c "
+ "JOIN c.products prods "
+ "WHERE c.id = 1 AND prods.price > :price";
I don't have the objects I wanted.
I could create an Entity named CaddieProduct, but I would like something cleaner.

Fetch database records using hibernate without using Primary Key

I want to fetch records from database without the primary key, for a table that i have created. Here is the model class for it.
#Entity
#Table(name = "DOCTORS_INFORMATION")
public class DoctorInformation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "DOCTOR_ID")
private int ID;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "ADDRESS_LINE1")
private String addressLine1;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "CITY")
private Location location;
//along with setters and getters
It is very difficult to remember the Doctor_Id for every doctor, and logically First_Name or anything cannot be made a primary key. All the tutorials and lecture videos that i have gone through fetch records using session.get where session is an object of class Session. In the source code of Session class all the overloaded methods of get mandatory take id as a parameter...
Is their a workaround for the above problem?
There are multiple options. All of the following examples search for a doctor whose lastName contains Johnson.
Criteria
String lastNameToSearchFor = "Johnson";
List doctors = session.createCriteria(DoctorInformation.class)
.add( Restrictions.like("lastName", "%"+lastNameToSearchFor+"%") ) // the leading wild card can become a problem since it cannot be indexed by the DB!
.addOrder( Order.asc("lastName") )
.list();
http://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/Criteria.html
HQL
String lastNameToSearchFor = "Johnson";
String hql = "FROM " + DoctorInformation.class.getName() + " di WHERE di.lastName LIKE :lastName ORDER BY di.lastName ASC";
Query query = session.createQuery(hql);
query.setParameter("lastName", "%" + lastNameToSearchFor + "%"); // again, the leading wild card may be a problem
List doctors = query.list();
http://www.tutorialspoint.com/hibernate/hibernate_query_language.htm
Native SQL
String lastNameToSearchFor = "Johnson";
String sql = "SELECT * FROM DOCTORS_INFORMATION WHERE lastName LIKE :lastName ORDER BY lastName ASC";
Query query = session.createSQLQuery(sql).addEntity(DoctorInformation.class);
query.setString("lastName", "%" + lastNameToSearchFor + "%"); // again, the leading wild card may be a problem
List doctors = query.list();
If you want to add capability to search with, let's say firstName, then you might want to look into Hibernate's Disjunction: Creating a hibernate disjunction query programatically

Categories