I'm using jpa EntityManager with hibernate in my java spring application. Suppose I have a user entity like below:
public class User {
private Long id;
...
#ManyToOne
private Address address
}
And I have Custom user dto object for passing into client:
public class UserDTO {
private Long id;
private AddressDTO address;
...
}
And I have a UserRepository that exceute normal jpql query with EntityManager and Query.
Note, I need to have custom dto, because my dto has some fields that does not exist in entity and must be calculated in query. Now my question: is there any way with EntityManager that map flat query result into my nested UserDTO? In fact, I need to map result of address in AdressDTO inside UserDto and so on.
Note: I want to use jpql not native sql query.
You can construct DTO right in JPQL.
Here is an example.
select new your.package.UserDTO(u.id, a.country, a.city, a.street)
from User u join u.address a
where ...
Such query returns List<UserDTO>.
Of course UserDTO has to have appropriate constructor:
public UserDTO(Long id, String country, String city, String street){
this.id = id;
this.address = new AddressDTO(country, city, street);
}
You're on the right way.
You really need to fetch User and then convert it to UserDTO.
Don't build DTO within your queries.
For that conversion you need Java Mapper. I prefer MapStruct but there is a plenty of such tools (ModelMapper, Dozer etc).
MapStruct is smart enough to manage nested objects as well.
Related
I have to MySQL tables TICKET-is the parent and USER-is the child, in a many-to-one relationship.
=TICKET=
PK(ID)
summary
message
FK(user_id) references USER(user_id)
=USER=
PK(ID)
email
password
And the JPA entities
#Entity
class TICKET {
#Id
private Integer ID;
private String summary;
private String message;
#ManyToOne
#JoinColumn(name="user")
private USER user;
}
#Entity
class USER {
#Id
private Integer ID;
private String email;
private String password;
}
If i make a query to get a ticket by ID it will return also the user information (USER.ID, USER.email, USER.password) which is not good.
ticketsCrudRepository.findById(ticketId);
What i want is to get a table that looks like this:
TICKET.ID | summary | message | USER.email
I know how to do it in MySQL but JPA it's to much for me. I don't want to use JPQL or native query language.
Any suggestions?
When you fetch a Ticket you also get a User since #ManyToOne by default has FetchType.EAGER. You can change this by changing the anotation to #ManyToOne(fetch = FetchType.LAZY)
To do the search you chould try somethig like
public List<Object[]> getTikcetsForUser(final User user) {
String hql = "select t.id, t.summary, t.message, u.email "
+ "from Tikcet t, User u "
+ "where ticket.user = :user";
Query<Object[]> query = getSession().createQuery(hql, Object[].class);
query.setParameter("user", user);
return query.getResultList();
}
The returned List will contain arrays with 4 fields (t.id, t.summary, t.message, u.email).
Please let me know if you find this useful.
:)
The solution i was looking for was Spring Data JPA Projections. They can be interfaces or classes with getters that match the columns you want to get from the database. For detailed informations check the Spring Framework documentation here.
Using Spring Data Projections is one option, but you will at some point run into the limitations it has. If you reach that point, you can look into Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(TICKET.class)
public interface TicketDto {
#IdMapping
Long getId();
String getName();
UserDto getUser();
#EntityView(USER.class)
interface UserDto {
#IdMapping
Long getId();
String getEmail();
}
}
Or even simpler in your case
#EntityView(TICKET.class)
public interface TicketDto {
#IdMapping
Long getId();
String getName();
#Mapping("user.email")
String getUserEmail();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TicketDto a = entityViewManager.find(entityManager, TicketDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Is there a way to select only some columns from a table using jpa?
My tables are huge and I am not allowed to map all the columns in my entities. I tried to create an entity (as a side note, I don't have PKs in my tables):
#Entity
#Table(name = "SuperCat")
#Getter
#Setter
public class Cat{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name="nameCat")
private String name;
}
and then in my repository to
public interface CatRepository extends
CrudRepository<Cat, Long> {
#Query(
"SELECT name FROM Cat")
Page<Cat> getAlCats(Pageable pageable);
This is only a simple example, but the idea is the same. I have searched a lot and I found projections, but there you need to map the whole table, then I found native queries, but still doesn't apply. I know I can return an Object and the other solution is to use query with NEW and create my own object (no #entity, like a pojo). But is there a way that I can do this using jpa, to be able to use repository and services, if I am creating my own pojo then i will create a #transactional class put the queries (with NEW) there and this is it. I don't like this approach and I don't think that the jpa does't allow you to select only some columns, but I didn't find a proper way.
Maybe you will ask what is the result if I am doing like this:
I get this error: "Cannot create TypedQuery for query with more than one return using requested result type [java.lang.Long]"
(For new queries, I am talking about : http://www.java2s.com/Tutorials/Java/JPA/4800__JPA_Query_new_Object.htm maybe I was not clear)
You can do the same by using below approach.
Just create a constructor in entity class with all the required parameters and then in jpa query use new operator in query like below.
String query = "SELECT NEW com.dt.es.CustomObject(p.uniquePID) FROM PatientRegistration AS p";
TypedQuery<CustomObject> typedQuery = entityManager().createQuery(query , CustomObject.class);
List<CustomObject> results = typedQuery.getResultList();
return results;
And CustomObject class should look like below with the constructor.
public class CustomObject {
private String uniquePID;
public CustomObject(String uniquePID) {
super();
this.uniquePID = uniquePID;
}
public String getUniquePID() {
return uniquePID;
}
public void setUniquePID(String uniquePID) {
this.uniquePID = uniquePID;
}
}
spring-data-jpa projection not need to map the whole table, just select the necessary fileds :
// define the dto interface
public interface CatDto {
String getName();
// other necessary fields
...
}
#Query(value = "select c.name as name, ... from Cat as c ...)
Page<CatDto> getAllCats(Pageable pageable);
By this way, CatDto is an interface and it only includes some fileds part of the whole table. Its fields name need to match the select field's alias name.
Currently I use a lot of queries which use constructors for building value objects in JPQL as below
#Query("SELECT new com.DocDTO(d.documentId, d.docType) FROM Document d where d.parentId=:parentId")
Set<DocDTO> getDocsWithinFolder(#Param("parentId") Long parentId);
But as code gets complex, I have a need to build objects with various combinations of constructor parameters, leading to the classic telescoping problem.
As explained in Effective Java (Item1) is there a way to build a JPQL query by passing a factory method instead of a constructor ? I am thinking something along the lines of
#Query("SELECT DocDTO.query1(d.documentId, d.docType) FROM Document d where d.parentId=:parentId")
Set<DocDTO> getDocsWithinFolder(#Param("parentId") Long parentId);
and then build the appropriate static factory method query1 inside the DocDTO class. Is this possible in JPQL ?
You can use Dynamic projection to solve this problem. Dynamic projection let you to change return type of single query dynamically. To better understand this lets take a example of this User entity:
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
// setter and getters
}
If you want to get only name of a user using dynamic projection first you will need to create a interface like this:
public interface Name {
String getLastName();
String getFirstName();
}
In your repository you will need to create query like this:
<T> List<T> findByLastName(String lastName, Class<T> type);
or with #Query
#Query("select u.firstName,u.lastName from User u where lastName=?1")
<T> List<T> findByLastName(String lastName,Class<T> type);
In your service:
List<Name> name = findByLastName("xyz",Name.class);
I'm just getting started with Spring/Hibernate and I'm trying to understand how hibernate maps entities to tables. Right now I'm working with the following classes to test out CRUD operation with hibernate:
#Entity
#Table(name = "Users")
public class UserEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String firstName;
private String lastName;
private int age;
//Getters, setters, etc.
}
#Repository
public class UserDAOImpl implements UserDAO {
#Autowired
HibernateTemplate hibernateTemplate;
#Override
public UserEntity getUser(String firstName, String lastName) {
List<UserEntity> users = hibernateTemplate.find("from UserEntity u where u.firstName = ? and u.lastName = ?", firstName, lastName);
return (users.size() == 0 ? null : users.get(0));
}
#Override
public void saveOrUpdate(UserEntity user) {
hibernateTemplate.saveOrUpdate(user);
}
}
The code above is working fine, but I'm confused how UserEntity is being mapped to a table. More specifically, when calling hibernateTemplate.find("from UserEntity u where u.firstName = ? and u.lastName = ?", firstName, lastName) I get the following message in the log:
Hibernate: select userentity0_.id as id0_, userentity0_.age as age0_, userentity0_.firstName as firstName0_, userentity0_.lastName as lastName0_ from Users userentity0_ where userentity0_.firstName=? and userentity0_.lastName=?
When calling hibernateTemplate.find() I'm required to specify the table UserEntity whereas the log reports it's querying the table Users (as I specified with the #Table annotation).
Ideally I'd like to be consistent throughout my program and refer to the UserEntity table as Users rather than UserEntity. Is there some way I can do this? If so can I do it with HQL or would I need to write native SQL queries instead?
When you write HQL you are querying against the OO model. So in this case, your class name UserEntity is used. However, hibernate will translate this into a native SQL query and thus in the logs you will see references to users table. I'm not sure what your question is though. If the classname is UserEntity then throughout your application you should use UserEntity. I think that going into native SQL is only necessary when the HQL does not do what you want.
Vincent explained the matter, I'll just add some some clarifications.
The #Table annotation is optional, and merely indicates what the underlying table is (by default it is the same as the entity name (passing through a translation mechanism).
But the idea of hibernate (and orm layers) is to let the developer write code without any reference to the underlying relational model whatsoever. And that's what you should try to do - "forget" about the relational model beneath and use only the object model.
I would like to write a hql query using a dynamic instantiation with a list as one of it's parameters.
Simplified example:
A HQL query with a dynamic instantiation:
select new x.y.UserDto(u.name, u.contacts) from User u where u.xyz=:param1 ...
and my dto class constructor is:
public class UserDto {
private String name;
private List contacts;
public UserDto(String name, List contacts) {
this.name = name;
this.contacts = contacts;
}
...
}
And the entity mapping:
public class User {
#olumn(name="NAME")
String name;
#ManyToMany(targetEntity= Contacts.class, fetch = FetchType.EAGER)
#JoinTable(name="USER_DEPARTMENT_CONTACTS",
joinColumns=#JoinColumn(name="DEPARTMENT_ID"),
inverseJoinColumns=#JoinColumn(name="USER_ID"))
private List<Contacts> contacts;
...
}
So as you can see all I want is to create a new object that has some properties and collections of an entity.
I can understand that Hibernate would need one or more queries to achieve this as this would generate multiple result rows for each entity.
Does anyone knows if it is possible to create a new object which is a combination of properties and collections?
Sorry but it is not possible. According to JPA specification,
The type of the query result specified by
the SELECT clause of a query is AN
ENTITY abstract schema type, A
STATE-FIELD type - NOT A COLLECTION -,
the result of an aggregate function,
the result of a construction
operation, OR SOME SEQUENCE OF THESE.
You could use the following instead:
select DISTINCT new x.y.UserDto(u) from User u LEFT JOIN FETCH u.contacts
So, this way you would have the users with your contacts fetched