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.
Related
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.
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
I'm working with spring-boot and angular5 , i have this entity in spring :
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Contrat implements Serializable{
#Id #GeneratedValue
private Long id;
private Date dateDebut ;
private Date dateFin ;
#ManyToOne
#JoinColumn(name = "Id_Project")
#JsonBackReference(value="projet-contrat")
private Project project;
#ManyToOne
#JoinColumn(name = "Id_AppUser")
#JsonBackReference(value="appuser-contrat")
private AppUser appUser;
}
A repository :
public interface ContratRepo extends JpaRepository<Contrat,Long> {
public Page<Contrat> findByAppUser(#Param("userApp") AppUser userApp, Pageable pageable);
}
As the fetch.lazy is the default one , when i try to call the method findByAppUser i get as result :
{id: 1, dateDebut: 1526083200000, dateFin: 1526083200000}
Which is normal , what i want for my case is to load also the object 'project' that exists in the entity , but i don't wan't to use the fetch.EAGER , any solution for this goal ?
Your entity is one-many relationship object. If you don't use EAGER, spring data will get the object without related member object. And if you get that with contract.getProject().getName(), then another query will be sent to get that member.
If you log the SQL, you can see that, there will be 2 queries. But if you set the field as EAGER, there will be only 1 query. You can get improvement obviously.
But you should not use EAGER always. If in 90% of time, you just need the Contract object, but no need the project data of it. It is a waste of time to get that. Because in SQL, it will relate 2 tables and get all columns of data.
SO, you should make this decision based on your usage of this entity.
[Updated based on comment]
You can use Query to write your sql expression. for example, I have a method to get the entity with detail:
#Query("select s from Contract s left join fetch s.project pr where s.id = ?1 ")
Contract findOneWithDetail(Long id);
If I need to get the detail in ONE sql, I can use this method. If I don't need the project detail, I just use findOne(Long id), which is provided interface.
And, if you just want to get some columns, you need to define a DTO, with a constructor, and write your method like this:
#Query("SELECT NEW com.mypackage.dto.ContractDTO(s.id, s.name, s.status) FROM Contract AS s WHERE s.status = ?1")
List<ContractDTO> findDTOAllByStatus(String status);
Provide the query in your repo method, e.g. (syntax may be wrong, just show you the idea)
public interface ContratRepo extends JpaRepository<Contrat,Long> {
#Query(query="from Contrat c left join fetch c.project " +
"where c.userApp = :userApp")
public Page<Contrat> findByAppUser(#Param("userApp") AppUser userApp, Pageable pageable);
}
I have entity class User
#Entity(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(unique = true, nullable = false)
private String email;
/** skipped **/
And stateless EJB DAO class(I am using jpa/hibernate) with method
#Override
public User findByEmail(String name) {
TypedQuery<User> query = entityManager.createQuery("from User where email= :email", User.class);
query.setParameter("email", name);
return query.getSingleResult();
}
When I execute this method from page(using jsf), it works fine. The problem occurse when I execute this method from WebService EJB bean. I've got following exception
org.hibernate.hql.internal.ast.QuerySyntaxException: User is not mapped [from User where email= :email]
My web service is very simple:
#Stateless(name = "webServiceBean")
#WebService(serviceName = "webService")
public class MyWebService{
#EJB
private UserDao dao;
public String findUser(String email) {
return dao.findByEmail(email);
}
}
Application is deployed on JBoss7. I am using EJB 3.1 and hibernate 4.3
Does anybody knows what is the reason of this exception? And why it occurs only in WebService?
Make sure User, along with the package name, is listed in hibernate config file.
Either use just #Entity or #Entity(name = "User"). You have named your entity as user but in your query trying to refer to it as User.
Check the docs for the #Entity's name attribute.
public abstract java.lang.String name
(Optional) The entity name. Defaults to the unqualified name of the entity class. This name is used to refer to the entity in queries. The name must not be a reserved literal in the Java Persistence query language.
I want to create Many-One Mapping between two tabels, Expense(ID, NAME, CATEGORY) and
Category(ID, NAME).
In my class i have created a field 'Category category' and its setters and getters.
I did them after seeing some stuff from internet. What are all the changes i have to do in my Category.java class. For now, its looks like,
public class Category{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int catId;
private String catName;
public Category() {
}
public int getCatId() {
return this.catId;
}
public void setCatId(int catId) {
this.catId = catId;
}
public String getCatName() {
return this.catName;
}
public void setCatName(String catName) {
this.catName = catName;
}
}
I dont want to do mappings with xml config. I think, annotations is good for a beginner like me.
And my Old! SQL query looks like,
SELECT EXPENSES.EXPNS_ID, EXPENSES.CAT_ID, EXPENSES.NAME, CATEGORY.CAT_NAME FROM EXPENSES INNER JOIN CATEGORY ON EXPENSES.CAT_ID = CATEGORY.CAT_ID WHERE USER_NAME="+currentUserName
How to use inner join in Hibernate?
Any Suggestions!!
Thanks!
Update
Thanks for all answerers,
I tried what you told and it returns a empty list.
To, test i set the 'userName=Tamil' which is in the table.
The query generated by Hibernate is looks like below,
select expens0_.expnsId as expnsId1_, expens0_.catId as catId1_, expens0_.category_catId as category7_1_, expens0_.userName as userName1_ from Expens expens0_ inner join Category category1_ on expens0_.category_catId=category1_.catId where expens0_.userName=?
As a beginner, i have some doubts in JPQL, I want catName from Category[catId, catName] table. And the catId is also available in Expens[expnsId, catId, userName].
By adding the below lines in Expens.java class, how it will give me catName along with the other variables in the Expens table.
#ManyToOne
private Category category
// getters, setters
I cant able to understand it. Without understanding this i cant move further, i have to give more mappings in my project. If clear with this mapping, i can move to the rest with confidence.
The query i used is pascal's version: Query query = hSession.createQuery("SELECT e FROM Expens e JOIN e.category c WHERE e.userName = :userName").setParameter("userName", userName);
For me, the query generated by hibernate is looks like same as my Old SQl query. I cant able to find problem here.
Actually, a big part of the documentation that would be useful in your case is located in the Hibernate Annotations Reference Guides (links provided below). Reading it would be very worth it.
That being said, regarding your specific question, the simplest possible mapping would be:
#Entity
public class Expense {
#Id #GeneratedValue
private Long;
#ManyToOne
private Category category
// getters, setters
...
}
That's all.
If you want to make it bi-directional, you'll have to add a OneToMany on the other side (and don't forget the mappedBy element since the association is bidirectional):
#Entity
public class Category {
#Id #GeneratedValue
private Long id;
#OneToMany(mappedBy="category")
private Set<Expense> expenses = new HashSet<Expense>();
....
}
And a possible JPQL query would be:
SELECT e FROM Expense e JOIN e.category c WHERE e.username = :username
Update: Hibernate and JDBC are different. With Hibernate, you need to think objects and the above HQL query (which was more an example) will actually return a List<Expense>. To get a category name, iterate over the results and navigate through the association. For example:
List<Expense> expenses = ... // some code to retrieve a list by username
for (Expense expense : expenses) {
System.out.println(expense.getCategory().getName());
}
References
2.2. Mapping with JPA (Java Persistence Annotations)
2.2.5.2. Many-to-one
As Bozho suggested,
#ManyToOne(fetch=FetchType.EAGER) // Gonna be eager by default anyway
#JoinColumn(name="CATEGORY_ID")
private Category category;
Plus this in your Category class to make it bidirectional,
#OneToMany(mappedBy="category")
private List<Expense> expense;
You need not do an inner join like that. When you query the expense, the related category will automatically get loaded eagerly, most likely using join.
In your Expense class have:
#ManyToOne
#JoinColumn(name="CATEGORY_ID")
private Category category
As pointed in the comments, if you need to access all expenses in a given category, i.e. have the one-to-many relationship, you can have:
#OneToMany
private List<Expense> expenses;
I, for example, prefer to use as little #OneToMany mappings as possible - you'd have to manager eager/lazy loading, at some point limiting the number of results, etc. For them I tend to use HQL queries that fetch the subset of objects (expenses in your case) that I need.