Custom #Query in JPA - mysql

I am trying to do a #Query in JPA that does not return a Entity from my Model.
In my Model I have BudgetPlan, BudgetGeschaftsfeld, BudgetMarke, BudgetKampagne, BudgetBucket, Kosten Entities which have a #OneToMany relationship.
So Budgetplan has many BudgetGeschaftsfeld which has many BudgetMarke and so on.
I want to return a CustomKosten with all couple of the standard Kosten attributes, and the ID to each "Parent ID". A Pojo of the Class would look like this.
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class CustomKosten {
private long id;
private String name;
private double betrag;
private String status;
private Date local_date;
private long idBucket;
private String bucket;
private long idKampagne;
private String kampagne;
private long idMarke;
private String marke;
private long idGeschaeftsfeld;
private String geschaeftsfeld;
private long idPlan;
private String plan;
}
I have accomplished the SQL query in MySQL and it gets the result I want. Which looks like this.
public interface CustomKostenRepository extends JpaRepository<CustomKosten, Long> {
#Query(nativeQuery = true, value="select id, name, betrag, status, local_date, idbucket, bucket, idkampagne, kampagne, idmarke, marke, idgeschaeftsfeld, geschaeftsfeld, idplan, plan from \r\n"
+ "(select * from (select * from (select * from (select * from (select pid as pidgeschaeftsfeld, id as idgeschaeftsfeld, name as geschaeftsfeld from budman_db.budget where dtype=\"Budgetgeschaftsfeld\") as tgeschaeftsfeld \r\n"
+ "left join (select id as idplan, name as plan from budman_db.budget where dtype=\"Budgetplan\" ) as tplan on tgeschaeftsfeld.pidgeschaeftsfeld= tplan.idplan) as t1\r\n"
+ "left join (select pid as pidmarke, id as idmarke, name as marke from budman_db.budget where dtype=\"Budgetmarke\") as tmarke on t1.idgeschaeftsfeld= tmarke.pidmarke) as t2\r\n"
+ "Left join (select pid as pidkampagne, id as idkampagne, name as kampagne from budman_db.budget where dtype=\"Budgetkampagne\") as tkampagne on t2.idmarke= tkampagne.pidkampagne ) as t3\r\n"
+ "left join (select pid as pidbucket, id as idbucket, name as bucket from budman_db.budget where dtype=\"Budgetbucket\") as tbucket on t3.idkampagne= tbucket.pidbucket) as t4\r\n"
+ "left join (select * from budman_db.kosten ) as tkosten on t4.idbucket= tkosten.pid")
public List<CustomKosten> getCustomKostenKomplex();
}
is the result I'm trying to get even possible?
I am getting this Error when running my code.
Any advice is much appreciated
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.bm.ent.kosten.CustomKosten
Edit:
After following the Example Eugene adviced im getting another error.
Caused by: java.lang.IllegalArgumentException: Could not locate appropriate
constructor on class : com.bm.ent.kosten.CustomKosten
I'm guessing it has something to do with the Type of each field. And I think I don't have to put a type on those which have a primitive class.
note: I purposely left out the date to test. Any advice?
#Entity(name= "Kosten")
#Table(name= "kosten")
#NamedNativeQuery(name = "CustomKostenAll",
query= "select id, name, betrag, status, idbucket, bucket, idkampagne, kampagne, idmarke, marke, idgeschaeftsfeld, geschaeftsfeld, idplan, plan from \r\n"
+ "(select * from (select * from (select * from (select * from (select pid as pidgeschaeftsfeld, id as idgeschaeftsfeld, name as geschaeftsfeld from budman_db.budget where dtype=\"Budgetgeschaftsfeld\") as tgeschaeftsfeld \r\n"
+ "left join (select id as idplan, name as plan from budman_db.budget where dtype=\"Budgetplan\" ) as tplan on tgeschaeftsfeld.pidgeschaeftsfeld= tplan.idplan) as t1\r\n"
+ "left join (select pid as pidmarke, id as idmarke, name as marke from budman_db.budget where dtype=\"Budgetmarke\") as tmarke on t1.idgeschaeftsfeld= tmarke.pidmarke) as t2\r\n"
+ "left join (select pid as pidkampagne, id as idkampagne, name as kampagne from budman_db.budget where dtype=\"Budgetkampagne\") as tkampagne on t2.idmarke= tkampagne.pidkampagne ) as t3\r\n"
+ "left join (select pid as pidbucket, id as idbucket, name as bucket from budman_db.budget where dtype=\"Budgetbucket\") as tbucket on t3.idkampagne= tbucket.pidbucket) as t4\r\n"
+ "left join (select * from budman_db.kosten ) as tkosten on t4.idbucket= tkosten.pid",
resultSetMapping= "CustomKostenMapping")
#SqlResultSetMapping(name = "CustomKostenMapping",
classes = {
#ConstructorResult(targetClass = CustomKosten.class,
columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "betrag"),
#ColumnResult(name = "status", type = Status.class),
// #ColumnResult(name = "local_date"),
#ColumnResult(name = "idbucket"),
#ColumnResult(name = "bucket", type = String.class),
#ColumnResult(name = "idkampagne"),
#ColumnResult(name = "kampagne", type = String.class),
#ColumnResult(name = "idmarke"),
#ColumnResult(name = "marke", type = String.class),
#ColumnResult(name = "idgeschaeftsfeld"),
#ColumnResult(name = "geschaeftsfeld", type = String.class),
#ColumnResult(name = "idplan"),
#ColumnResult(name = "plan", type = String.class),
}
)
})
#Getter
#Setter
#NoArgsConstructor
public class Kosten {...}

First, if you want to use the CustomKosten class as return type of your repository, you need to place annotate it with #Entity so that it becomes a managed type for JPA / Hibernate.
This however would only work if you have a table with a structure corresponding to your entity. In your case, you query several columns from different tables. Therefore, you need to specify how Hibernate should map all the returned columns to your Java class. I recommend this tutorial on SQL result set mapping by one of the Hibernate maintainers Thorben Janssen. If you already use Hibernate 6, you could also use a TupleTransformer as shown here.

Related

case insensitive issue while fetching records from child table using HQL

I have a parent table and a child table where I am only getting 1 record from child table but not getting case insensitive matched record which is a mixed string. I am expecting it should return 2 records.
Below is the code for the same.
//parent Table
#Entity
#Table(name = "employee")
public class Employee implements Serializable {
#Id
#Column(name = "employeeID")
private String employeeID;
#Column(name = "name_first")
private String nameFirst;
#Column(name = "name_last")
private String nameLast;
}
//Child Table
#Entity
#Table(name = "employee_salary")
public class EmployeeSalary implements Serializable {
#EmbeddedId
private EmployeeSalaryPK employeeSalaryPKCompositeKey;
#Column(name = "salaryBracket")
private String salaryBracket;
}
#Embeddable
public class EmployeeSalaryPK implements Serializable {
#Column(name = "employeeID")
private String employeeID;
#Column(name = "salary")
private String salary;
}
In employee_salary table I have two records (as shown below) but while fetching it using HQL only one record is coming with an actual match but case insensitive record is not coming.
Employee Record:- ABC John Kramer
employee_salary table record:-
ABC 100900
aBc 76770
I am using simple HQL query (see below code) but getting only first record whenever I want to get both record as employeeID is abc.
String hqlQuery = " FROM " + Employee.class.getName() + " E WHERE E.employeeID= :EMPLOYEEID";
Session session = entityManager.unwrap(Session.class);
List<?> responseList = session.createQuery(hqlQuery).setParameter("EMPLOYEEID", "ABC").list();
To get all entities by case insensetive String id you have to convert id to same case (lowercase or uppercase) on both sides of the WHERE clause equality operator
String hqlQuery = " FROM " + Employee.class.getName() + " E WHERE lower(E.employeeID) = :EMPLOYEEID";
Session session = entityManager.unwrap(Session.class);
List<?> responseList = session.createQuery(hqlQuery).setParameter("EMPLOYEEID", "ABC".toLowerCase()).list();

select max native query hibernate

I am trying to get the max value of a column in a table using a native query with the #Query annotation
I tried to derive it from the examples here: https://www.baeldung.com/spring-data-jpa-query
#Query(value = "SELECT max(i.sequence) " +
"FROM invoices as i " +
"WHERE i.fleet_id = ?1", nativeQuery = true)
Long findMaxSequence(String fleetId);
i ve also tried:
#Query(value = "SELECT max(i.sequence) " +
"FROM invoices as i " +
"WHERE i.fleet_id = :fleetId", nativeQuery = true)
Long findMaxSequence(#Param("fleetId") String fleetId);
When i call my method as :
long maxSeq = invoiceRepository.findMaxSequenceForFleetId(invoice.getFleetId());
I get a NullPointerException. Any ideas why?
Invoice entity looks like this :
#Entity
#Table(name = "invoices"}
public class Invoice implements Serializable {
#Id
private String id;
#Column
private long sequence;
#Column(length = 12)
private String fleetId;
// ...
}
The issue was due to the fact that the database was empty so the query was returning null
and basic types such as long cannot be assigned to null values. Weirdly the compiler did not complain..
I modified my code as below:
Long maxSeq = invoiceRepository.findMaxSequenceForFleetId(invoice.getFleetId());
if(maxSeq == null){
maxSeq = 0L;
}

Spring data JPA , result object has numbers instead of column names

i would like some help trying to do the following.I want to get the number of purchases of each user in the database grouped by his name and id.But it's very hard to do compared to simple sql.
I have the following code in my PurchaseRepository that extends CrudRepository
#Query("SELECT p.user.name as Name,p.user.id as Id,Count(p) as Purchases from Transaction p GROUP BY p.user.id")
List<Object> purchaseNumOfEachUser();
First of all im not sure if this is the right query because i wanted to do Count(*) but says its not valid.
Secondly , the object i get returned when converted to Json via a controller is like
0:{
0:"John",
1:2, //id
2:5 //purchases
}
What i would like to get is
0:{
"Name" : "John",
"Id" : 1 ,
"Purchases" : 2
},
1:{
....
}
Any ideas?
1) The query:
SELECT p.user.name as Name, p.user.id as Id, Count(p) as Purchases
from Transaction p GROUP BY p.user.id
should be
SELECT p.user.name as Name, p.user.id as Id, Count(p) as Purchases
from Transaction p GROUP BY p.user.name, p.user.id
You must group by all rows you are selecting.
2) the result
The result of the query is List if you want to have something meaningful you should consider the constructor expression that let's you create your own objects.
For example
package mypackage;
public class PurchaseInfo {
private final String name;
private final Integer id;
private final Long count;
public PurchaseInfo(String name, Integer id, Long count) {
this.name = name;
this.id = id;
this.cound = count;
}
// getters
}
This class can then be use in the query (please notice the fully qualified class name after NEW):
SELECT NEW mypackage.PurchaseInfo(p.user.name, p.user.id, Count(p))
from Transaction p GROUP BY p.user.name, p.user.id
The result will then be List and you will get it nicely serialized to JSON.
Read more about the Constructor Expression here:
https://vladmihalcea.com/the-best-way-to-map-a-projection-query-to-a-dto-with-jpa-and-hibernate/

#ManyToOne with native query leads to N+1 select

I know the problem with N+1 select is well-known, but trying using different fetching strategies does not help to avoid it when I use native query.
I have 2 entities: A and B that correspond to the mysql tables A and B.
Several rows in table A could have the same b_id, that's why I use #ManyToOne annotation.
#Entity
#Table(name = "A")
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.JOIN)
private B b;
}
I also create entity B.
#Entity
#Table(name = "B")
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String url;
}
I need to find all rows in A that are linked only for 1 row in table B. So I create native query in my ARepository
public interface ARepository extends JpaRepository<A, Integer> {
#Query(value = "SELECT a.id, a.b_id, a.name, b.url "
+ "FROM a "
+ "JOIN b ON a.b_id = b.id "
+ "GROUP BY a.b_id "
+ "HAVING count(a.b_id) = 1, nativeQuery = true)
List<A> getLinkedOnce();
But when when I run my restcontroller that just call getLinkedOnce method from repository, I see that in console, 1-st my query is called, then there are number of selects for each row in A that are end with
from B b0_ where b0_.id=?
I try to use different approaches, LAZY, EAGER and it does not work. I think because there is a usage of native query. But maybe the reason is another.

Cannot Seem to retrieve data from table in the order of the database table's column order

I want to to retrieve data from table in the order of the database table's column order.
Suppose my sql query is
String sql_string = "select * "
+ "from CUSTOMER_INFO "
+ "order by customer_last_name, customer_first_name";
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
List<Map<String,Object>> results = session.createSQLQuery(sql_string)
.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
.list();
In database, the table's column order is like A,B,C,D.
But when I retrieve the data and iterate through it, the entrySet is like C,A,D,B. (int,float,float,String)
I think the data is being retrieved based on its datatype.
I need the retrieved entrySet in the same order as it exists in database's table.
I also tried specifying the column names in select query which was of no use.
CUSTOMER_INFO is model class mapped to sqllite through hibernate.
#Entity
#Table(name = "CUSTOMER_INFO")
public class CustomerInfo{
#Column
private int C;
#Column
private float A;
#Column
private String B;
#Column
private float D;
//getters and setters
}
Using Sqllite 3.6, hibernate, JSP.
Any help is appreciated.