Spring Data Jpa to get data which doesn't have entity representation - java

I'm trying to find the best way to map my data on ORM. I have a query which gets me data from MySQL database which look like
SELECT d.ID AS Id,
equipment.EQUIP_ID,
equipment.EQUIP_REFERENCE
FROM
tbl_devices d
INNER JOIN
tbl_equipment equipment ON equipment.EQUIP_ID = d.DEV_ID
What would be the most optimal way to get these data with Spring boot and Spring data??
Should I use #Query annotation and execute this or somehow create entities for Equipment and Devices tables and then use JPQL/HQL to join tables in a query, but then how should I map the result??
Thanks in advance.

You can use JdbcTemplate (import from org.springframework.jdbc.core.JdbcTemplate) to run the SQL statement above you wrote. After you can create a pojo to map result into it via BeanPropertyRowMapper. For example:
final String sql = "SELECT d.ID AS Id, equipment.EQUIP_ID, equipment.EQUIP_REFERENCE FROM tbl_devices d INNER JOIN tbl_equipment equipment ON equipment.EQUIP_ID = d.DEV_ID";
YourPojo result = jdbcTemplate.query(
sql,
new BeanPropertyRowMapper<>(YourPojo.class)
);
Pojo class maybe like following:
#Data
public class YourPojo {
private Long id;
private Long equipId;
private Your_type equip_reference_name;
}

A quick and dirty solution would be to use projections.
First, you create a projection interface:
public interface DeviceDetails {
Long getId();
Long getEquipId();
String getEquipReference();
}
You then modify the query to match column aliases with the projection properties:
SELECT d.ID AS id,
equipment.EQUIP_ID as equipId
equipment.EQUIP_REFERENCE As equipReference
...
Finally, you put the query method in a repository of your choice:
#Query(value =..., nativeQuery = true)
List<DeviceDetails> findDeviceDetails();

Related

Hibernate - How to select just the foreign key value using Criteria Query without doing a Join?

So there is this similar (almost identical) question: How to select just the foreign key value using Criteria Query? but I need to avoid doing the join.
So I have two tables, one with a FK to the other
(Service)
id
name
account_id
(Account)
id
name
Service class has the FK defined this way:
#Entity
public class Service extends BaseDbEntity implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private Account account;
...
and what I want to do is query all the Service's that correspond to a given account, having the accountId, without doing a JOIN, because the id is already in the Service table.
Now, my criteria query looks like this:
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Service> criteria = cb.createQuery(Service.class);
Root<Service> root = criteria.from(Service.class);
criteria
.select(root)
.where(cb.equal(root.join("account").get("id"), accountId));
session.createQuery(criteria).getResultStream();
This ends up generating this query:
Hibernate: select service0_.id as id1_3_, service0_.name as name4_3_, service0_.account_id as account_id6_3_ from Service service0_ inner join Account account1_ on service0_.account_id=account1_.id where account1_.id=?
Which doesn't make sense.. it does a join on a field and then just creates a where clause for that field.
If I do:
root.get("account_id")
it just throws an exception saying the field is not available.
What is the correct way to avoid this?
Ok I just found the answer to my question:
Instead of doing
root.join("account").get("id")
I just needed to do:
root.get("account").get("id")
which avoids performing the JOIN.

Hibernate Types framework throws "PSQLException: ERROR: cannot cast type bigint to interval" when using the PostgreSQLIntervalType with a native SQL

I'm a beginner in the Spring Framework.
I'm using the hibernate-types for casting a PostgreSQL Interval into a Java Duration Object.
For selecting all rows this works fine. I've created a data model for the specific table and used the TypeDef annotation for the mapping:
#Entity
#Table(name = "test")
#Typedef(
typeClass = PostgreSQLIntervalType.class,
defaultForType = Duration.class
)
#Getter #Setter
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private Duration flightDuration;
}
But now I want to create a custom native query. I want to select all rows from table Test, where the flightDuration is a RequestParam of a webservice.
Like this as a basic example (the real query is much more complex and needs some special postgres functions):
public interface TestRepository extends JpaRepository<Test, int> {
#Query( value = SELECT * FROM test t WHERE flight_duration = ?1 , nativeQuery = true )
List<Test> findByFlightDuration(Duration flightDuration);
}
But here is my problem. I don't know how to use the PostgreSQLIntervalType for casting the Duration into a PostgreSQL Interval.
If i execute the query like the way above, i'll get the following error:
org.postgresql.util.PSQLException: ERROR: cannot cast type bigint to interval
So where i have to set an annotation (or whatever) like the TypeCast in the Test Class, that the Duration will be casted into a PostgreSQLIntervalType?
There is no reasonfor using a native query. You can simply JPQL.
#Query( value = "FROM test t WHERE flight_duration = ?1")
List<Test> findByFlightDuration(Duration flightDuration);
If you REALLY need the nativeQery, because you need it, then you have to cast the integer in your query into an Interval of PostgreSQL.
I suppose you provide seconds in your query, and the ?1 is the number of seconds your flight will take.
Try the following:
#Query(value = SELECT * FROM test t WHERE flight_duration = (?1 || ' secs') , nativeQuery = true )
List<Test> findByFlightDuration(int flightDurationInSec);
More information here: https://www.postgresql.org/message-id/Pine.LNX.4.44.0410051443550.13144-100000#matrix.gatewaynet.com
But I suggest you to review if you really need this or not. Maybe you can try with JPQL. Let us know.
For native SQL queries, you need to use the Hibernate-specific setParameter Query method to provide the Hibernate Type that's needed to handle custom column types:
List<Test> tests = entityManager.createNativeQuery("""
SELECT *
FROM test t
WHERE flight_duration = :duration
""", Test.class)
.unwrap(org.hibernate.query.Query.class)
.setParameter("duration", flightDuration, PostgreSQLIntervalType.INSTANCE)
.getResultList();
This way, Hibernate will know how to handle the duration query parameter.

Efficient JPQL query for retrieving complex entities

I'm quite the rookie with JPA/JPQL, so please excuse me if this question is not crystal clear.
I am trying to find an efficient JQPL query in order to get all records of a complex object.
(ie. represented by multiple tables, with several one-to-many relationships - see simplified example below):
class ComplexObject {
private Set< SubOject1> so1 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject1>...
}
class SubObject1 {
private Set< SubOject2> so2 ...
.....
#OneToMany(fetch = FetchType.LAZY)
public Set< SubOject2>...
}
I am using the following JPQL query :
select distinct CO
from ComplexObject CO
left join fetch CO.so1 SO1
left join fetch SO1.so2
The query is run on a stateless session, in order to get a de facto snapshot of the current data in the DB, which is detached from the entity manager (hence the usage of left join fetch).
Unfortunately, I've encountered 2 problems :
Since the complex object contains multiple instances of so1, and each so1 instance contains multiple instances of so2, the underlying translation to SQL queries generates a specific select query per row of the product of all the table joins - a very wasteful solution. Is there a way to reduce the number of internal select queries? (This seems like the dreaded N+1 queries problem).
The JPQL query returns a ComplexObject instance per internal SQL query on the product of all the table joins - which means multiple references to the ComplexObject instances. Why does this happen on a 'select distinct' query?
The JPA framework I am using is hibernate, and the DB is HyperSQL.
The (1) issue turned out to be related to using the p6spy logging framework, which printed out all the results from a large DB table. The logging format led to an incorrect assumption that many queries where being executed.
While trying to fine tune performance, using native queries did not appear to have better performance then using JPQL queries.
Using a Native Query also resulted in Object typed results, which required post processing.
You can use View Objects to receive only the columns what you want:
StringBuilder sb = new StringBuilder();
sb.append(" SELECT new ").append(ObjectVO.class.getName()).append("(co.someInfo1, co.someInfo2, so1.someInfo )");
sb.append(" FROM ComplexObject co ");
sb.append(" JOIN co.subOject1s so1 ");
sb.append(" LEFT JOIN so1.so2 so2 ");
sb.append(" WHERE so1.id = :idSo1 AND so2 = :someThing");
Query q = em.createQuery(sb.toString());
q.setParameter("idSo1", idSo1);
q.setParameter("someThing", someThing);
List<ObjectVO> listResult = q.getResultList();
The ObjectVO class:
public class ObjectVO {
private String info1;
private Long info2;
private String info3;
public PedidoModel(String info1, Long info2, String info3){
this.info1 = info1;
this.info2 = info2;
this.info3 = info3;
}
}

DOT node with no left-hand-side using HQL with join

I am trying to find the maximum value of a Date column in mySQL DB using hibernate query language with join
#Query("select o.techid, CAST(MAX(o.last_modified) AS DATE)
from com.dw.model.user.User as u
left join com.dw.model.order.Order as o
on u.username=o.techid group by o.techid")
List<User> findUsers();
Model class =
#Entity(name = "orders")
#Scope("prototype")
#Component
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
public class Order {
#Id
private long orderNumber;
private Date last_modified;
I am getting this error :-
Caused by: java.lang.IllegalStateException: DOT node with no left-hand-side!
Can anyone please help me out by telling me how to write this in Hibernate?
Remove your package names. An entity is defined by its name only. Dots are used for properties and links between tables (when defined as a #ManyToOne* property).
select o.techid, CAST(MAX(o.last_modified) AS DATE)
from User as u
left join Order as o
on u.username=o.techid group by o.techid
Think Classes and Properties, not columns, when you write HQL.
Try following solution, this should work
SELECT o.techid, CAST(MAX(o.last_modified) AS DATE)
FROM User u LEFT JOIN u.order o GROUP BY o.techid

can hibernate hsql automatically map the query result to a class?

I wrote a hsql:
String queryString = "select t1.a, t1.b, t2.c from table1 t1, table2 t2 where t1.id = t2.id";
and then I have a class:
class test{
String a;
String b;
String c
....//other getter and setter
}
I tried:
List = getHibernateTemplate().find(queryString);
this doesn't work, when I use test object in jsp page, it will throw out exception.
I have to manually create a test object:
List<Object[]> list = getHibernateTemplate().find(queryString);
test.seta(list.get(0)[0]);
is it possible for hibernate to automatically map the class for me in hsql ?
If you have a mapping for both table1 and table2 (see Prashant question above) you can do something like:
String queryString = "select t1 from table1 t1
inner join t1.table2 t2";
After you run the query you should have a list of t1 objects.
for(Table1 t1:listOfTable1Objects) {
t1.getA(); //for example or whatever you want to do with your object.
}
The Problem is that you do not write a HQL query. You just write a normal SQL query. In HQL, because the hibernate make the mapping from table to class, you cannot make a projection. So, if you write something like
String query = "FROM Class1 WHERE ome_condition;
without the SELECT clause, the Hibernate will be able to convert the result in the proper object.
You can see more about this here: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/queryhql.html
If you dont have a mapping, you may create a auxiliary class for this. Say ResultClass. Then you add #NamedNativeQuery and #SqlResultSetMapping annotations to the class:
#NamedNativeQuery(name="queryHehehe", query="select t1.field1 f1, t2.field2 f2 from table1 t1, table2 t2", resultSetMapping="mappingHehehe")
#SqlResultSetMapping(name="mappingHehehe", entities={
#EntityResult(entityClass=my.clazz.AuxiliaryClass.class, fields = {
#FieldResult(name="id", column="f1"),
#FieldResult(name="other_property", column="f2")
}),
})
public class AuxiliaryClass {
public Long id;
public String other_property;
}
I have never used this, but can work. Good luck.
If you need a query to return values from multiple tables and create an object of an unmapped class, then you need to either do what you're doing here, or use a ResultTransformer.
In order to do this with HibernateTemplate, you'll need to change the way you use the template, possibly using execute(HibernateCallback action), as you'll need to convert the sql query to a Criteria as described in Hibernate Reference Native SQL Chapter.
If you do want to try this, you'll probably want to use an AliasToBeanResultTransformer or AliasToBeanConstructorResultTransformer rather than writing your own transformer.

Categories