Join two tables with hibernate - java

I am looking to create a DAO which represents a join of two tables with Java Hibernate. Here is the SQL I'd like to represent (Postgres 9.6 incase that matters):
SELECT tableOneValue, tableTwoValue
FROM table_one, table_two
WHERE table_one_filter = 2 AND table_one_id = table_two_id;
These tables have a OneToOne relationship.
Table1.java
#Entity
#Data
#Table(name="table_one")
public class TableOneDao implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "table_one_id")
private int tableOneId;
#Column(name = "table_one_value")
private String tableOneValue;
#Column(name = "table_one_filter")
private int tableOneFilter;
}
Table2.java
#Entity
#Data
#Table(name="table_two")
public class TableTwoDao implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "table_twp_id")
private int tableTwpId;
#Column(name = "table_two_value")
private String tableTwoValue;
}
I'm very new to hibernate so maybe this isn't the right way to think with it. What I would love to do is define a SomeDao class where I can do: daoManager.findAll(SomeDao.class, Pair.of("tableOneFilter", 2));
This would return a List<SomeDao> where we get all the rows that satisfy tableOneFilter == 2.

You need to use the #OneToOne and #JoinColumn annotation.
Pay special attention to the userDetail attribute mapping.
For example, the user class:
#Entity
#Table(name = "USERS")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "USR_ID")
private long id;
#Column(name = "USERNAME", nullable = false, unique = true)
private String username;
#Column(name = "PASSWORD")
private String password;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="USR_DET_ID")
private UserDetail userDetail;
// Add Constructor, Setter and Getter methods
}
And this user details class:
#Entity
#Table(name = "USER_DETAILS")
public class UserDetail {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "USR_DET_ID")
private long id;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "EMAIL")
private String email;
#Column(name = "DBO")
private LocalDate dob;
// Add Constructor, Setter and Getter methods
}
Check the full code here.

Here is a JPA query which will work with your existing entity structure with the latest version of hibernate.
SELECT t1.tableOneValue, t2.tableTwoValue
FROM TableOneDao AS t1 JOIN TableTwoDao AS t2 ON t1.table_one_id = t2.table_two_id
WHERE t1.table_one_filter = ?

You can write a JPQL statement which is much better. Here is the sample solution:
SELECT NEW com.test.package.dao(t1.valueOne, t2.valueTwo)
FROM table_one t1 JOIN table_two t2
WHERE t1.filter = 2 AND t1.id = t2.id;
Please refer to this link and jump to the section where it mentions Result Classes (Constructor Expressions). Hope it helps. Thanks.

Related

spring data jpa find all by example nested collection property

I have two objects. The company that can have multiple nested addresses.
#Entity
#Data
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "phone")
private String phone;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Address> addresses;
}
Address class looks like this:
#Data
#Entity
#Table(name = "address")
#ToString(exclude = "company")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "postal_code")
private String postalCode;
#Column(name = "city")
private String city;
#Column(name = "street")
private String street;
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
private Company company;
}
I want somehow if it's possible, make a dynamic query that searches through the nested collection property. I made a search method which uses example matcher but the result is wrong. Every time I got everything from DB, not only company with address postal code that I'm looking for.
My search method looks like this:
#PostMapping("/search")
public List<Company> search(#RequestBody final Company company){
return companyRepository.findAll(Example.of(company,
ExampleMatcher.matchingAny()
.withIgnoreNullValues()
.withIgnorePaths("id")
.withStringMatcher(ExampleMatcher.StringMatcher.STARTING)));
}
In my database, I have two objects and this is the result of the search:
As you can see I received everything from DB instead of the only first company which address postal code starts with 1.
Hi you can use Specification<T>
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
For this you need to extend from interface JpaSpecificationExecutor:
public interface UserRepository extends JpaRepository<User> ,JpaSpecificationExecutor<User>{
}
And you also need to implement your custom Specification<T>
And then you can use repository.findAll(your impleneted Specification);
Spring docs :
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html
I think this is helpful.

Mapping a database view entity with a simple entity and pass to DTO using Spring Data

I'm just learning Spring Data. I want to map a database view Entity with a simple Entity and pass to DTO which will contain columns both entities. I understand that I can use a special database view but I need to map precisely entities of Spring Data.
I have a database view Entity "MentorStudents":
#Entity
#Table(name = "mentor_students")
#Immutable
public class MentorStudents implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "mentor_id", updatable = false, nullable = false)
private Long mentorId;
//This entity I need to map
private Mentor mentor;
#Column(name = "active_students")
private Integer activeStudents;
public MentorStudents() {
}
//getters, setters, equals, hashCode
}
A database view sql of an above entity is:
SELECT id AS mentor_id, active_students
FROM mentor
LEFT JOIN ( SELECT mentor_id, count(mentor_id) AS active_students
FROM contract
WHERE close_type IS NULL
GROUP BY mentor_id) active ON mentor.id = active.mentor_id
ORDER BY mentor.id;
And I have a simple Entity "Mentor":
#Entity
#Table(name = "mentor")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Mentor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#NotNull
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "patronymic")
private String patronymic;
#Column(name = "phone")
private String phone;
#NotNull
#Column(name = "email", nullable = false)
private String email;
#Column(name = "skype")
private String skype;
#Column(name = "country")
private String country;
#Column(name = "city")
private String city;
#Column(name = "max_students")
private Long maxStudents;
//getters, setters, equals, hashCode
I have to get a DTO which contains all Mentor fields and an "activeStudents" MentorStudents field without a "mentorId" field. How do it?
Use spring data projection:
public interface YourDto {
// all Mentor get fields
String getFirstName();
...
// activeStudents get field
Integer getActiveStudents();
}
public interface YourRepository extends JpaRepository<YourEntity, Integer> {
#Query(value = "select ...(all fields match YourDto) from Mentor m, MentorStudents s where m.id = s.mentorId and m.id = ?1")
Optional<YourDto> findMyDto(Integer mentorId);
}

Hibernate - multi-language tables

I'm trying to build a multi-language database, so I've used this database design as a approach for mine.
Now I've two problems/questions:
I want to retrieve all LocalizedEvent for a given language and given categoryId. How can I make a inner join over the LocalizedCategory table with Hibernate Criteria API?
With SQL I would make this statement to get all LocalizedEvent + LocalizedCategory:
SELECT * FROM event e
INNER JOIN
localized_event le ON (le.event_id = e.event_id)
INNER JOIN
localized_category lc ON (lc.category_id = e.category_id)
WHERE
le.locale = 'de' AND lc.locale = 'de'
My current approach looks like this without getting the LocalizedCategory (with Criteria API):
Criteria c = session.createCriteria(LocalizedEvent.class, "localizedEvent");
c.createAlias("localizedEvent.event", "event");
c.createAlias("event.category", "category");
c.add(Restrictions.eq("category.categoryId", categoryId));
c.add(Restrictions.eq("localizedEvent.locale", language));
I think my mapping is not 100% correct. The entity LocalizedEvent should have a property localizedCategory, but I don't want to save the ID of this localizedCategory (therefore I'm using the #Transient annotation) in the LocalizedEvent table, e.g. using a ManyToOne relation (joining LOC_CATEGORY_ID). But I think it's not possible to do this, isn't it? I would have to map this transient field to LocalizedEvent "manually", because Hibernate is not supporting this mapping (if I'm right).
(Using JDBC this property/mapping would cause no problems, because I can easily make my inner joins and assign the property localizedCategory to the LocalizedEvent in a RowMapper or so).
My entities looks like this:
Event
#Entity
#Table(name = "EVENT")
public class Event {
#Id
#GeneratedValue
#Column(name = "EVENT_ID", unique = true)
private Long eventId;
#Column(name = "DATE")
private Date date;
#OneToMany(mappedBy = "event")
private Set<LocalizedEvent> localizedEvents;
#ManyToOne
#JoinColumn(name = "CATEGORY_ID")
private Category category;
}
LocalizedEvent
#Entity
#Table(name = "LOCALIZED_EVENT")
public class LocalizedEvent {
#Id
#GeneratedValue
#Column(name = "LOC_EVENT_ID")
private Long locEventId;
#ManyToOne
#JoinColumn(name = "EVENT_ID")
private Event event;
#Transient
private LocalizedCategory localizedCategory;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "LOCALE")
private String locale;
}
Category
#Entity
#Table(name = "CATEGORY")
public class Category {
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID", unique = true)
private Long categoryId;
#OneToMany(mappedBy = "category")
private Set<LocalizedCategory> localizedCategories;
#OneToMany(mappedBy = "category")
private Set<Event> events;
}
LocalizedCategory
#Entity
#Table(name = "LOCALIZED_CATEGORY")
public class LocalizedCategory {
#Id
#GeneratedValue
#Column(name = "LOC_CATEGORY_ID")
private Long locCategoryId;
#ManyToOne
#JoinColumn(name = "CATEGORY_ID")
private Category category;
#Column(name = "NAME")
private String name;
#Column(name = "LOCALE")
private String locale;
}

JPA Entity and HQL JOIN command

I'm trying to achieve something like sql command below by using HQL and JPA.
Instead of "SELECT user_id..." I need SELECT OBJECT(o).
SELECT user_id FROM posix_user o INNER JOIN postgre_user n ON n.id=o.user_id WHERE n.name='USERNAME2'
I have some problems with this part of the code in JPA DAO:
public List<PosixUserEntity> listPosixUsers(final String uid_number) {
final StringBuilder queryString = new StringBuilder("SELECT OBJECT(o) FROM ");
queryString.append(this.entityClass.getSimpleName());
queryString.append(" o JOIN com.services.dao.user.jpa.UserEntity n ON (n.id=o.user_id) WHERE n.name LIKE :uid_number");
final Query findByNameQuery = entityManager.createQuery(queryString.toString()).setParameter("uid_number", uid_number);
return findByNameQuery.getResultList();
}
JOIN ON is not allowet here and I did not know how to replace it.
Also how can I replace com.services.dao.user.jpa.UserEntity by something cleaner.
There is my Entites, they may need to be improved:
#Entity
#Table(name = "posix_user")
public class PosixUserEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
//#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private String user_id;
#Column(name = "uid_number")
private String uid_number;
#Column(name = "home_directory")
private String home_directory;
#Column(name = "login_shell")
private String login_shell;
#Column(name = "group_id")
private String group_id;
//getters,setters....
#Entity
#Table(name = "postgre_user")
#SQLDelete(sql = "update postgre_user set status = 'removed' where id = ?")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name", unique = true, nullable = false)
private String name;
#Column(name = "password")
private String password;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private UserStatus status;
#Column(name = "firstname")
private String firstName;
#Column(name = "lastname")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "usertype")
private String userType;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserRoleTargetGroupEntity> userRoleTargetGroupEntity;
#Column(name = "last_login")
private String lastLogin;
#Column(name = "previous_login")
private String previousLogin;
#JsonIgnore
#Column(name = "change_password_flag")
private Boolean userPasswordResetFlag;
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
private PosixUserEntity posixUserEntity;
You may also need to know that FOREIGN KEY (user_id) REFERENCES postgre_user (id) - it should look like that
Can you know how can I modify my SELECT?
I've tested a simplified version of your classes
#Entity
#Table(name = "posix_user")
public class PosixUserEntity {
#Id
#Column(name = "user_id")
private Long user_id;
// getter + setter
}
#Entity
#Table(name = "postgre_user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
private PosixUser posixUserEntity;
// getter + setter
}
And this JPQL query works as expected
String jpql = "SELECT p "
+ "FROM UserEntity n JOIN n.posixUserEntity p "
+ "WHERE n.name LIKE :uid_number)";
JOIN is allowed because you have mapped the relationship in UserEntity.
and you don't need to specify the complete name of your entity class.
Check if it has been included when you define your persistence unit.
Hope this helps.

Why does hibernate join all table, declared this particular join?

I'm using hibernate-core:3.3.1.GA
I have three mappings:
class PlayerAccount:
#Entity
#Table(name = "player_account")
public class PlayerAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(targetEntity = Player.class, fetch = FetchType.EAGER)
#JoinColumn(name="player_id")
private Player player;
//GET, SET
}
class PlayerAchievements:
#Entity
#Table(name="player_achievement")
public class PlayerAchievement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(targetEntity = Player.class, fetch = FetchType.EAGER)
#JoinColumn(name = "player_id")
private Player player;
//GET, SET
}
and class Player
#Entity
#Table(name = "player")
#Inheritance(strategy = InheritanceType.JOINED)
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "ps_id")
private String psId;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
//GET, SET
}
Issue:
When I try to write a simple criteria query like the following:
Criteria criteria = getSession().createCriteria(PlayerAccount.class);
criteria.add(Restrictions.eq("playerId", playerId));
criteria.list();
The resulting sql-query contains joins have the following view:
SELECT
-- columns to select
FROM player_account this_
LEFT OUTER JOIN player player2_
ON this_.player_id=player2_.id
LEFT OUTER JOIN external_partner_player player2_1_
ON player2_.id=player2_1_.player_id
LEFT OUTER JOIN player_achievement achievemen3_
ON player2_.id=achievemen3_.player_id
WHERE player_id = 2362189
The thing is player_achievements may contain more than one row with the same player_id value. Since we probably have a duplicated result in that criteria query. How to fix that using hibernate whithout the writing an sql-query?
Because the Fetch Types are eager. That means that you always want the entities loaded when you load the main entity.
You can use FetchType.LAZY for best perform. Here explains better: https://howtoprogramwithjava.com/hibernate-eager-vs-lazy-fetch-type/
You need to do SELECT DISTINCT, like this:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Categories

Resources