Cont. on jpa native query retrieve multiple entities
My database (testing)
company - ID {PK}, name
staff - ID{PK}, name, companyID{FK}
department - ID{PK}, name, companyID{FK}, staffID{FK}
project - ID{PK}, name, staffID{FK}
First, I want to backup the company table.
String query = "SELECT c.ID as companyid, c.name as companyname FROM company c";
Query q = em.createNativeQuery(query, "MAPPING");
List<CompanyDTO> list = q.getResultList();
Company.java
#SqlResultSetMapping(name = "MAPPING", classes = {
#ConstructorResult(
targetClass = CompanyDTO.class,
columns = {
#ColumnResult(name = "companyid"),
#ColumnResult(name = "companyname")
})
})
...
Second, I want to backup the staff table.
String query = "SELECT s.ID as staffid, s.name as staffname, s.companyID as companyID
FROM staff s JOIN company c ON c.ID = s.companyID";
Query q = em.createNativeQuery(query, "MAPPING");
List<CompanyDTO> list = q.getResultList();
Company.java
#SqlResultSetMapping(name = "MAPPING", classes = {
#ConstructorResult(
targetClass = CompanyDTO.class,
columns = {
#ColumnResult(name = "staffid"),
#ColumnResult(name = "staffname"),
#ColumnResult(name = "companyID")
})
})
...
Now I don't want to fix my table and class. How do I control the ? from the following?
SELECT ... FROM ?
List<?> list = q.getResultList();
Any help please...
Related
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.
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?
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{...}
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.
I'm wondering what's the benefit of using #SqlResultSetMapping and #ConstructorResult? Is it better, and if, why, from using JPQL SELECT NEW SomeConstructor ?
For example, is that better:
#Table(name = "person")
#Entity
#SqlResultSetMappings(
#SqlResultSetMapping(name = "contactLess",
classes = #ConstructorResult(columns = {
#ColumnResult(name = "id", type = Long.class),
#ColumnResult(name = "full_name", type=String.class)
}, targetClass = Person.class)
)
)
#NamedNativeQueries({
#NamedNativeQuery(name = "Person.findWithoutContacts",
query = "SELECT c.id, c.full_name FROM person c ",
resultSetMapping = "contactLess", resultClass = Person.class)
})
than this ( spring data in this case):
#Query("SELECT NEW Person(c.id, c.fullName) FROM Person c")
public List<Person> findAll();
If you have several JPQLs (and/or query builders) refering to one entity, than using annotations will make your code easier to refactor. JPQL also has no compile safety.