Best way to fetch data in SpringBoot by query from two tables - java

I have two tables:
A : x_id, emp_id, name, age
B: emp_id, company_id, location
I want to fetch data which contain columns 'x_id', 'emp_id', 'company_id', 'name' joining tables 'A' and 'B' using emp_id..What is the best way to get it?
Is it possible to fetch data without creating beans mapping A and B like
Can I create a bean 'Result' containing variables 'x_id', 'emp_id', 'company_id', 'name' and populate it and get list of 'Result' beans as my output?

Yes, first you have to create a model class which includes the required details as the attributes.
#SqlResultSetMapping(
name = "ResultMap",
classes = #ConstructorResult(
targetClass = A.class,
columns = {
#ColumnResult(name = "x_id", type = Long.class),
#ColumnResult(name = "emp_id", type = Long.class),
#ColumnResult(name = "company_id", type = Long.class),
#ColumnResult(name = "name", type = String.class)
}
)
)
public class ResultMap {
private BigInteger x_id;
private BigInteger emp_id;
private BigInteger company_id;
private String name;
public ResultMap(BigInteger x_id, BigInteger emp_id, BigInteger company_id, String name) {
this.x_id = x_id;
this.emp_id = emp_id;
this.company_id = company_id;
this.name = name;
}
}
Then, write a custom query in repository class to get required data. Return type will be List of Tuple.
#Query(
value = "SELECT a.x_id, a.emp_id, b.company_id, a.name \n" +
"FROM A as a, B as b \n" +
"WHERE a.emp_id = b.emp_id",
nativeQuery = true)
List<Tuple> findResultMaps();
Finally Map this List of Tuple to List of ResultMap where ever it used in.
List<Tuple> resultsMapTuples = resultMapDao.findResultMaps();
List<ResultMap> resultMaps = resultsMapTuples.stream()
.map(t -> new ResultMap(
t.get("x_id", BigInteger.class),
t.get("emp_id", BigInteger.class),
t.get("company_id", BigInteger.class)
t.get("name", String.class)
)).collect(Collectors.toList());
resultMapDao is the repository class that findResultMaps() method written in.

Related

How to create complex join between three tables using Hibernate?

I have three tables (I'll list only important fields):
Format
id
template_id
parameterisation_id
Param
name
value
parameterisation_id
ParamDict
name
template_id
I need to map params into formats into one-to-many association, but they do not have direct connection and needed to be joined through ParamDict table.
I currently did it with #JoinTable like so:
#Entity
#Table(schema = "123", name = "format")
public class Format implements Serializable {
#Id
private Long id;
private String template_id;
private String parameterisation_id;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "paramDict", schema = "123",
joinColumns = {#JoinColumn(name = "template_id", referencedColumnName =
"template_id",insertable = false,updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "param_name", referencedColumnName =
"param_name",insertable = false,updatable = false)})
private Set<Param> params = new HashSet<>()
}
And in sql it looks like this
Select * from
Format f
left join ParamDict pd on f.template_id = pd.template_id
left join Param p on p.name = pd.name;
But now i need to add another condition in Param join:
Select * from
Format f
left join ParamDict pd on f.template_id = pd.template_id
left join Param p on (p.name = pd.name and p.parameterisation_id = f.parameterisation_id);
I tried to add where clause into #Query annotation in my Repository:
public interface FormatRepository extends Repository<Format, String> {
#Query("select n from format f "
+ "left join fetch f.params p "
+ "where f.parameterisation_id in (?1) "
+ "and f.parameterisation_id = p.parameterisation_id"
)
Set<Format> findBypParameterisationIdIn(List<String> ids);
}
And it created right sql query, but Hibernate does not count that condition (f.parameterisation_id = p.parameterisation_id) when mapping query results into class objects, and i get wrong results in Param set (there are items in the params set, that does not have the same parameterisation_id as Format).
How can i do it via Hibernate mapping?

How can I map a result set to custom POJO in JPA

I need to fetch 6 columns by joining 3 different tables. I have declared them as NamedNativequery on top of the entity class and I have used create named query method form JPA. When I try fo fetch the result set i get the list of array objects instead of the List of objects of POJO type. is there any external mapping should I be defining in order to map the result set to an external POJO?
You certainly can. This should help:
#NamedNativeQuery(query = "SELECT t1.col1, t2.col2 FROM t1 JOIN t2 ON ...", name = "MyNamedQuery", resultSetMapping = "MyPojoMapper")
#SqlResultSetMapping(name = "MyPojoMapper", classes = #ConstructorResult(
targetClass = MyPojo.class,
columns = {
#ColumnResult(name = "col1", type = String.class),
#ColumnResult(name = "cols", type = String.class)
}))
Then use it as such:
NativeQuery query = session.getNamedNativeQuery("MyNamedQuery");
MyPojo result = (MyPojo) query.getSingleResult();
You can use projection to specify what properties you want to get
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
or directly get with JPQL:
Repository.java
#Repository
public class CustomRepositoryImpl {
#Autowired
private EntityManager entityManager;
public List<Dto> find() {
var query = "SELECT new Dto(
x.Field1,
y.Field2,
z.Field3,
...)
FROM XxxEntity x
LEFT JOIN YyyEntity y
LEFT JOIN ZzzEntity z"
var jpqlQuery = entityManager.createQuery(query);
return jpqlQuery.getResultList();
}
}
Dto.java
public class Dto {
// Must have parameterized constructor with all fields what used in Repository
public Dto(int field1, String field2, String field3, ...) {
}
}

JPA Query with Joins and a Custom Obj that is not Table

I am doing a join with a native query. The returned object is a new object that contains all the attributes in the query. But it throws an error saying it can't bind object[] to CombinedObj. What do I do?
public interface SettingsRepository extends JpaRepository<Setting, Long> {
#Query(value = "select s.some_value, o.other_value from settings s inner join other_table o on o.id = s.other_table_id where user_id = :user_id", nativeQuery = true)
List<CombinedObj> find(#Param("user_id") int userId);
}
You can't simply project your columns and expect that jpa infer this columns to result object.
It's possible to solve this using a Named Native Query. Like this:
#Entity
#Table(name = "settings")
#SqlResultSetMapping(
name = "yourResultSetMapping",
classes = {
#ConstructorResult(
targetClass = CombinedObj.class,
columns = {
#ColumnResult(name = "some_value"), #ColumnResult(name = "other_value")
}
)
}
)
#NamedNativeQuery(
name = "Settings.getNativeQuery",
query = "select s.some_value, o.other_value from settings s inner join other_table o on o.id = s.other_table_id where user_id = :user_id",
resultSetMapping = "yourResultSetMapping"
)
public class Settings{...}

How do I subquery a map for multiple key value combinations in JPA 2.0?

I have the following entity (example):
#Entity
#Table(name = "person")
public class Person implements Serializable {
#Id
#Column(name = "person_id", columnDefinition = "UUID")
private UUID userId;
#Column(name = "name")
private String name;
#ElementCollection
#MapKeyColumn(name = "phonetype")
#Column(name = "number")
#CollectionTable(name = "person_phones", joinColumns = #JoinColumn(name = "userId"))
private Map<String, String> phoneNumbers;
}
Now, the phoneNumbers are String, String in this example. Let's assume the key is the type (like "mobile", "home", "office", "fax", "pager"...) and the value is the actual number in any text format.
I'd like to query for a person which has two phone numbers:
Select * From person where
in his phone_numbers exists phonetype = 'home' and number = '0-123-456'
and also in his phone_numbers exists phonetype = 'mobile' and number = '9-876-421'
(and possibly, dynamically others)
and name = 'John'
I already constructed a sql subquery which works:
select home.userId from
(
(SELECT userId from person_phones
where (phonetype = 'home' and number = '0-123-456'))
) as home,
(
(SELECT userId from person_phones
where (phonetype = 'mobile' and number = '9-876-421'))
) as mobile
where home.userId = mobile.userId
As said, this is just a sql subquery. I'm writing JPA 2.1 criteria query in my project. And this seems oddly complicated. Can anyone give me a hint?
Had a similar problem, solved it using multiple inner joins rather than sub queries.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Person-Test");
EntityManager em = emf.createEntityManager();
Map<String, String> phoneNumbers = new HashMap<>();
phoneNumbers.put("home","0-123-456");
phoneNumbers.put("mobile","9-876-421");
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> personRoot = query.from(Person.class);
query.select(personRoot);
phoneNumbers.forEach((k, v) -> {
MapJoin<Person, String, String> phoneNrJoinJoin = personRoot.joinMap("phoneNumbers");
phoneNrJoinJoin.on(cb.equal(phoneNrJoinJoin.key(), k), cb.equal(phoneNrJoinJoin.value(), v));
});
query.where(cb.equal(personRoot.get("name"), "John"));
List<Person> people = em.createQuery(query).getResultList();
This results in the following hibernate query (renamed aliases for clarity)
SELECT person.person_id, person.name
FROM person
INNER JOIN person_phones a
ON person.person_id = a.userid
AND (a.phonetype = ? AND a.NUMBER = ?)
INNER JOIN person_phones b
on person.person_id=b.userId
and (b.phonetype=? and b.number = ? )
WHERE
person.name = ?;
Which returns all tuples of type person where all the mentioned phone numbers match.

Spring data throws an exception:" No property [property_name] found for type [Object]!"

I would like a Spring Data interface to return a non-entity object.
I have googled and found a solution that I should use named queries with the result set mapping to a non-entity class.
However, I do resieve the described above exception. What causes this exception?
Here is my implementation of the sql result set mapping and named native query:
#SqlResultSetMapping(
name = "revenue",
classes = {
#ConstructorResult(
targetClass = Revenue.class,
columns = {
#ColumnResult(name = "date"),
#ColumnResult(name = "revenue")
}
)
}
)
#NamedNativeQuery(name = "calculateRevenueForGasStationName",
query = "select fueling.date, fueling.price*fueling.volume as revenue from gas_station " +
"left join fueling " +
"on gas_station.id = fueling.gas_station " +
"where fueling.date between ?1 and ?2 and gas_station.name=?3 " +
"group by fueling.date",
resultSetMapping = "revenue")
Here is the spring data interface method that should be linked to a name = "calculateRevenueForGasStationName"
#Query(nativeQuery = true)
List<Revenue> calculateRevenueForGasStationName(Date from, Date to, String name);
It seems problem with your class properties. Can you please revisit your Revenue.class property details.
Please make sure
1) "date" property has declared as java.util.Date
2) revenue has declared as Double
3) Sample code is
`#SqlResultSetMapping(
name = "revenue",
classes = { #ConstructorResult(
targetClass = Revenue.class,
columns = {
#ColumnResult(name = "date", type = java.util.Date.class),
#ColumnResult(name = "revenue",type = Double.class)
} ) } )`

Categories

Resources