I have the following database model:
A
aId
AB
aId
bId
B
bId
status
In a Spring data Specification, I want to return the instances of A when B.status is 'X'.
The JPQL code is the following:
select a from A a where a in
(select ab.id.a from AB ab where ab.id.b.status= :status)
These are the model classes:
#Entity
public class A {
private Long aId;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.a")
private Set<AB> ab;
}
#Entity
public class B {
private Long bId;
private String Status;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.b")
private Set<AB> ab;
}
#Entity
public class AB {
private ABPK id;
}
public class ABPK {
#ManyToOne
#JoinColumn(name="aId")
private A a;
#ManyToOne
#JoinColumn(name="bId")
private B b;
}
How would be the JPA Criteria in the Spring Specification?
public class ASpecifications {
public static Specification<A> test(final String status) {
return new Specification<Party>() {
#Override
public Predicate toPredicate(Root<A> a, CriteriaQuery<?> query, CriteriaBuilder cb) {
return null;
}
};
}
}
The Specification that returns instances of A using Criteria API is the following:
public class ASpecifications {
public static Specification<A> test(final String status) {
return new Specification<Party>() {
#Override
public Predicate toPredicate(Root<A> a, CriteriaQuery<?> query, CriteriaBuilder cb) {
Subquery<A> sq = query.subquery(A.class);
Root<AB> ab = sq.from(AB.class);
sq.select(ab.get(AB_.id).get(ABPK_.a));
sq.where(cb.equal(ab.get(AB_.id).get(ABPK_.b).get(B_.status), status));
Predicate p = cb.in(a).value(sq);
return cb.and(p);
}
};
}
}
There are some good examples included on a previous post that address exactly what you're trying to accomplish here: jpa-2-0-criteria-api-subqueries-in-expressions.
I suppose you wanted to select "A entities from AB entities where B is of provided status":
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> cq = cb.createQuery(A.class);
Root<AB> ab = cq.from(AB.class);
cq.select(ab.get("id").get("a"));
cq.where(cb.equal(ab.get("id").get("b.status"), status));
Select In - is an option but you could achieve same result with double join. And its easier to make such a jpa specification:
SELECT A.ID FROM A LEFT JOIN AB ON A.ID = AB.A_ID LEFT JOIN B ON AB.B_ID = B.ID WHERE B.STATUS = 'STATUS'
Method would look like this:
public static Specification<A> findB(String input) {
return new Specification<A>() {
#Override
public Predicate toPredicate(Root<A> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Join<A,AB> AjoinAB = root.joinList(A_.AB_LIST,JoinType.LEFT);
Join<AB,B> ABjoinB = AjoinAB.join(AB_.B,JoinType.LEFT);
return cb.equal(ABjoinB.get(B_.NAME),input);
}
};
}
Or shorter
public static Specification<A> findB(String input) {
return (Specification<A>) (root, cq, cb) -> {
Join<A,AB> AjoinAB = root.joinList(A_.AB_LIST,JoinType.LEFT);
Join<AB,B> ABjoinB = AjoinAB.join(AB_.B,JoinType.LEFT);
return cb.equal(ABjoinB.get(B_.NAME),input);
};
}
I know it's been a long time since this question arose, but I came across it when I tried to do the same. This is my solution that works and I hope it helps someone.
Entities below
#Entity
public class A {
#Id
private Long id;
private String name;
#OneToMany(mappedBy = "a")
private List<AB> abList;
}
#Entity
public class B {
#Id
private Long id;
private String status;
#OneToMany(mappedBy = "b")
private List<AB> abList;
}
#Entity
public class AB {
#Id
private Long id;
private String name;
#ManyToOne
#JoinColumn(name = "a_id")
private A a;
#ManyToOne
#JoinColumn(name = "b_id")
private B b;
}
Related
public class ChallengeDto {
private Long id;
private Category category;
private String title;
private String subTitle;
private boolean like;
private int totalScore;
private int requiredScore;
public ChallengeDto(Long id, Category category, String title, String subTitle, boolean like, int totalScore, int requiredScore) {
this.id = id;
this.category = category;
this.title = title;
this.subTitle = subTitle;
this.like = like;
this.totalScore = totalScore;
this.requiredScore = requiredScore;
}
}
I created challengeDto that include challenge's properties(id, category, title, subtitle, totalScore, requiredScore) and like property(can know that if i like challenge or not).
If I put like button, that information stored challengeLike table.
public class ChallengeLike {
#Id
#GeneratedValue
#Column(name = "challenge_like_id")
private Long id;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "challenge_id")
private Challenge challenge;
private LocalDateTime createDate;
}
Now I'm trying to write a code to retrieve challengeDto that checks if I clicked like or not, but I'm having a problem... I can't think of what kind of code to make.
#Repository
#RequiredArgsConstructor
public class ChallengeDtoRepository {
private final EntityManager em;
#Transactional
public List<ChallengeDto> findChallenges(Long userId) {
return em.createQuery(
"select new " +
"com.example.candy.controller.challenge.ChallengeDto(c.id,c.category,c.title,c.subTitle,????,c.totalScore,c.requiredScore)" +
" from Challenge c" +
" left join ChallengeLike cl on c.id = cl.challenge.id" +
" and cl.user.id = : userId", ChallengeDto.class)
.setParameter("userId", userId)
.getResultList();
}
}
try to rename the field to likeDone or something different than like, it makes the code ambiguous.
However, just simply do:
cl.likeDone
which means:
return em.createQuery(
"select new " +
"com.example.random.demo.dto.ChallengeDto(c.id,c.category,c.title,c.subTitle,cl.likeDone,c.totalScore,c.requiredScore)" +
" from Challenge c" +
" left join ChallengeLike cl on c.id = cl.challenge.id" +
" where cl.user.id = : userId", ChallengeDto.class)
.setParameter("userId", userId)
.getResultList();
However, try to use JPA if you don't have any mandatory condition to use native query or jpql.
JPA implementation:
#Repository
public interface ChallengeLikeRepository extends JpaRepository<ChallengeLike, Long> {
List<ChallengeLike> findAllByUser_Id(long userId);
}
Just call the repository method from service layer and map to your required dto:
public List<ChallengeDto> findChallenges(Long userId) {
List<ChallengeLike> entities = this.repository.findAllByUser_Id(userId);
return entities.stream().map(this::mapToDto).collect(Collectors.toList());
}
The mapToDto() method converts the entity to corresponding ChallengeDto
private ChallengeDto mapToDto(ChallengeLike x) {
return ChallengeDto.builder()
.category(x.getChallenge().getCategory())
.id(x.getChallenge().getId())
.like(x.isLikeDone())
.requiredScore(x.getChallenge().getRequiredScore())
.subTitle(x.getChallenge().getSubTitle())
.title(x.getChallenge().getTitle())
.totalScore(x.getChallenge().getTotalScore())
.userId(x.getUser().getId())
.build();
}
For your convenience, some properties has been added or changed in some classes. The #Builder annotation has been added to the ChallengeDto class. The rest of the corresponding entity and other classes:
a) ChallengeLike.java
#Entity
#Data
public class ChallengeLike {
#Id
#GeneratedValue
#Column(name = "challenge_like_id")
private Long id;
#ManyToOne
#JoinColumn(name = "user_id")
#JsonIgnoreProperties("challengeLikes")
private User user;
#ManyToOne
#JoinColumn(name = "challenge_id")
#JsonIgnoreProperties("challengeLikes")
private Challenge challenge;
private boolean likeDone;
private LocalDateTime createDate;
}
b) Challenge.java
#Entity
#Data
public class Challenge {
#Id
private Long id;
private Category category;
private String title;
private String subTitle;
private int totalScore;
private int requiredScore;
#OneToMany(mappedBy = "challenge", cascade = CascadeType.ALL)
#JsonIgnoreProperties("challenge")
private List<ChallengeLike> challengeLikes = new ArrayList<>();
}
c) Category.java
public enum Category {
CAT_A,
CAT_B
}
Update
If you want to fetch Challenge entity instead of ChallengeLike and map that to ChallengeDto, first implement ChallangeRepository:
#Repository
public interface ChallengeRepository extends JpaRepository<Challenge, Long> {
}
Add the fetchType to EAGER in Challange Entity class:
#OneToMany(mappedBy = "challenge", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonIgnoreProperties("challenge")
private List<ChallengeLike> challengeLikes = new ArrayList<>();
And to map the Challenge to ChallengeDto, you can add another mothod as follows:
private ChallengeDto mapToDto(Challenge x) {
return ChallengeDto.builder()
.category(x.getCategory())
.id(x.getId())
.like(!x.getChallengeLikes().isEmpty() && x.getChallengeLikes().get(0).isLikeDone())
.requiredScore(x.getRequiredScore())
.subTitle(x.getSubTitle())
.title(x.getTitle())
.totalScore(x.getTotalScore())
.userId(x.getUserId()) // if you have user reference in Challenge, remove this otherwise.
.build();
}
finally, to incorporate everything properly, change the caller:
public List<ChallengeDto> findChallenges(Long userId) {
List<Challenge> entities = this.repository.findAll();
List<ChallengeDto> entitiesWithoutChallengeLikes = entities.stream()
.filter(x -> x.getChallengeLikes() == null
|| x.getChallengeLikes().isEmpty())
.map(this::mapToDto).collect(Collectors.toList());
List<ChallengeDto> entitiesInferredFromChallengeLikes = entities.stream()
.filter(x -> x.getChallengeLikes() != null && !x.getChallengeLikes().isEmpty())
.flatMap(x -> x.getChallengeLikes().stream())
.map(this::mapToDto)
.collect(Collectors.toList());
entitiesInferredFromChallengeLikes.addAll(entitiesWithoutChallengeLikes);
return entitiesInferredFromChallengeLikes;
}
Final Update
Well, I finally understood properly what you expected. Adopt the following changes to the previous solution and you will get exactly what you want.
Change the 2 occurrence of the following in the findChallanges method:
.map(this::mapToDto)
To:
.map(x -> mapToDto(x, userId))
And the two mapToDto functions will be changed to follows:
private ChallengeDto mapToDto(ChallengeLike x, long userId) {
return ChallengeDto.builder()
.category(x.getChallenge().getCategory())
.id(x.getChallenge().getId())
.like(x.getUser().getId() == userId && x.isLikeDone())
.requiredScore(x.getChallenge().getRequiredScore())
.subTitle(x.getChallenge().getSubTitle())
.title(x.getChallenge().getTitle())
.totalScore(x.getChallenge().getTotalScore())
.userId(x.getUser().getId())
.build();
}
private ChallengeDto mapToDto(Challenge x, long userId) {
return ChallengeDto.builder()
.category(x.getCategory())
.id(x.getId())
.like(false)
.requiredScore(x.getRequiredScore())
.subTitle(x.getSubTitle())
.title(x.getTitle())
.totalScore(x.getTotalScore())
.userId(userId)
.build();
}
Entities:
#Entity
#Table(name = "shop")
public class Shop implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String address;
#OneToMany(mappedBy = "shop")
private List<Product> product = new ArrayList<>();
public Shop() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
#Entity
#Table(name = "product")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "disc_col")
public class Product implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal price;
#ManyToOne
private Shop shop;
public Product() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Shop getShop() {
return shop;
}
public void setShop(Shop shop) {
this.shop = shop;
}
}
#Entity
#DiscriminatorValue("loose")
public class LooseProduct extends Product {
private BigDecimal weight;
public LooseProduct() {
}
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
this.weight = weight;
}
}
#Entity
#DiscriminatorValue("liquid")
public class LiquidProduct extends Product {
private BigDecimal volume;
public LiquidProduct() {
}
public BigDecimal getVolume() {
return volume;
}
public void setVolume(BigDecimal volume) {
this.volume = volume;
}
}
Service:
public class ShopRepositoryImpl implements ShopRepositoryCustom{
#PersistenceContext
private EntityManager em;
#Override
public List<Shop> findShops(BigDecimal volume, BigDecimal weight, BigDecimal price) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Shop> cq = cb.createQuery(Shop.class);
Root<Shop> root = cq.from(Shop.class);
Join<Shop, Product> product = root.join("product", JoinType.LEFT);
Predicate p1 = cb.equal(cb.treat(product, LiquidProduct.class).get("volume"), volume);
Predicate p2 = cb.equal(cb.treat(product, LooseProduct.class).get("weight"), weight);
Predicate p3 = cb.equal(product.get("price"), price);
cq.where(cb.and(p3, cb.or(p1, p2)));
Query q = em.createQuery(cq);
return q.getResultList();
}
}
I have a problem that my query findShops generates too much joins:
select shop0_.id as id1_1, shop0_.address as address2_1, shop0_.name as name3_1 from shop shop0
left outer join product product1 on shop0_.id=product1_.shop_id
left outer join product product2 on shop0_.id=product2_.shop_id
left outer join product product3_ on shop0_.id=product3_.shop_id where product3_.price=1 and (product2_.volume=1 or product3_.weight=0)
It is InheritanceType.SINGLE_TABLE strategy so it shouldn't create three joins because there is just one table Product in database. Is there any way to optimize this?
Code from org.hibernate.query.criteria.internal.CriteriaBuilderImpl class:
#SuppressWarnings("unchecked")
private <X, T, V extends T, K extends JoinImplementor> K treat(
Join<X, T> join,
Class<V> type,
BiFunction<Join<X, T>, Class<V>, K> f) {
final Set<Join<X, ?>> joins = join.getParent().getJoins();
final K treatAs = f.apply( join, type );
joins.add( treatAs );
return treatAs;
}
The treat method creates new join from existing one. It is happening every time independent of inheritence type.
Next hibernate generates query and do not check duplicates in joins.
Do you have any idea how to prevent from generate additional joins when we use treat method?
I found the error report:
https://hibernate.atlassian.net/projects/HHH/issues/HHH-12094?filter=allissues&orderby=created%20DESC&keyword=treat
If you use JPA 2.1 onwards you can change the query like this:
select shop0_.id as id1_1, shop0_.address as address2_1, shop0_.name as name3_1
from shop shop0
left join product product1 on shop0_.id=product1_.shop_id
and (
(product1.disc_col = 'loose' and product1.weight = weight_var)
or (product1.disc_col = 'liquid' and product1.volume = volume_var)
);
For the implementation you would need to add the mapping of the disc_col column to the Product entity.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Shop> cq = cb.createQuery(Shop.class);
Root<Shop> root = cq.from(Shop.class);
Join<Shop, Product> product = root.join("product", JoinType.LEFT);
//add other conditions
product.on(
cb.and(
cb.or(
cb.and(cb.equal(product.get("discCol"),"liquid"),cb.equal(product.get("volume"),volumeVar)),
cb.and(cb.equal(product.get("discCol"),"loose"),cb.equal(product.get("weight"),weightVar))
)
)
);
Query q = em.createQuery(cq);
return q.getResultList();
I am taking this question Perform multi column search on Date, Integer and String Data type fields of Single Table? and This method must return a result of type Specification<Employee> in Java 8 further ahead.
Actually I wanted to search within association entity as well as a part of global search. Will that be possible using JPA 2 Specifications API ?
I've Employee and Department #OneToMany bi-directional relationship.
Employee.java
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "EMPLOYEE_ID")
private Long employeeId;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
#Column(name = "EMAIL_ID")
private String email;
#Column(name = "STATUS")
private String status;
#Column(name = "BIRTH_DATE")
private LocalDate birthDate;
#Column(name = "PROJECT_ASSOCIATION")
private Integer projectAssociation;
#Column(name = "GOAL_COUNT")
private Integer goalCnt;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "DEPT_ID", nullable = false)
#JsonIgnore
private Department department;
}
Department.java
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "DEPT_ID")
private Long departmentId;
#Column(name = "DEPT_NAME")
private String departmentName;
#Column(name = "DEPT_CODE")
private String departmentCode;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
#JsonIgnore
private Set<Employee> employees;
}
and I saved Data like below.
MyPaginationApplication.java
#SpringBootApplication
public class MyPaginationApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MyPaginationApplication.class, args);
}
#Autowired
private EmployeeRepository employeeRepository;
#Autowired
private DepartmentRepository departmentRepository;
#Override
public void run(String... args) throws Exception {
saveData();
}
private void saveData() {
Department department1 = Department.builder()
.departmentCode("AD")
.departmentName("Boot Depart")
.build();
departmentRepository.save(department1);
Employee employee = Employee.builder().firstName("John").lastName("Doe").email("john.doe#gmail.com")
.birthDate(LocalDate.now())
.goalCnt(1)
.projectAssociation(2)
.department(department1)
.build();
Employee employee2 = Employee.builder().firstName("Neha").lastName("Narkhede").email("neha.narkhede#gmail.com")
.birthDate(LocalDate.now())
.projectAssociation(4)
.department(department1)
.goalCnt(2)
.build();
Employee employee3 = Employee.builder().firstName("John").lastName("Kerr").email("john.kerr#gmail.com")
.birthDate(LocalDate.now())
.projectAssociation(5)
.department(department1)
.goalCnt(4)
.build();
employeeRepository.saveAll(Arrays.asList(employee, employee2, employee3));
}
}
EmployeeController.java
#GetMapping("/employees/{searchValue}")
public ResponseEntity<List<Employee>> findEmployees(#PathVariable("searchValue") String searchValue) {
List<Employee> employees = employeeService.searchGlobally(searchValue);
return new ResponseEntity<>(employees, HttpStatus.OK);
}
EmployeeSpecification.java
public class EmployeeSpecification {
public static Specification<Employee> textInAllColumns(Object value) {
return (root, query, builder) -> builder.or(root.getModel().getDeclaredSingularAttributes().stream()
.filter(attr -> attr.getJavaType().equals(value.getClass()))
.map(attr -> map(value, root, builder, attr))
.toArray(Predicate[]::new));
}
private static Object map(Object value, Root<?> root, CriteriaBuilder builder, SingularAttribute<?, ?> a) {
switch (value.getClass().getSimpleName()) {
case "String":
return builder.like(root.get(a.getName()), getString((String) value));
case "Integer":
return builder.equal(root.get(a.getName()), value);
case "LocalDate":
return builder.equal(root.get(a.getName()), value);//date mapping
default:
return null;
}
}
private static String getString(String text) {
if (!text.contains("%")) {
text = "%" + text + "%";
}
return text;
}
}
When I hit the /employees/{searchValue}, I want searching to be happened in Department Table along with Employee table (may be using Joins something like that). Is that possible ? If yes, how can we do that ?
Or:
Will this be good approach to put like here? Got reference from Using #Query
#Query("SELECT t FROM Todo t WHERE " +
"LOWER(t.title) LIKE LOWER(CONCAT('%',:searchTerm, '%')) OR " +
"LOWER(t.description) LIKE LOWER(CONCAT('%',:searchTerm, '%'))")
List<Todo> findBySearchTerm(#Param("searchTerm") String searchTerm);
Any pointers?
If you take a look at my post actually I have a solution for join
#Override
public Specification<User> getFilter(UserListRequest request) {
return (root, query, cb) -> {
query.distinct(true); //Important because of the join in the addressAttribute specifications
return where(
where(firstNameContains(request.search))
.or(lastNameContains(request.search))
.or(emailContains(request.search))
)
.and(streetContains(request.street))
.and(cityContains(request.city))
.toPredicate(root, query, cb);
};
}
private Specification<User> firstNameContains(String firstName) {
return userAttributeContains("firstName", firstName);
}
private Specification<User> lastNameContains(String lastName) {
return userAttributeContains("lastName", lastName);
}
private Specification<User> emailContains(String email) {
return userAttributeContains("email", email);
}
private Specification<User> userAttributeContains(String attribute, String value) {
return (root, query, cb) -> {
if(value == null) {
return null;
}
return cb.like(
cb.lower(root.get(attribute)),
containsLowerCase(value)
);
};
}
private Specification<User> cityContains(String city) {
return addressAttributeContains("city", city);
}
private Specification<User> streetContains(String street) {
return addressAttributeContains("street", street);
}
private Specification<User> addressAttributeContains(String attribute, String value) {
return (root, query, cb) -> {
if(value == null) {
return null;
}
ListJoin<User, Address> addresses = root.joinList("addresses", JoinType.INNER);
return cb.like(
cb.lower(addresses.get(attribute)),
containsLowerCase(value)
);
};
}
private String containsLowerCase(String searchField) {
return "%" + searchField.toLowerCase() + "%";
}
Here you can see how I search the users by their address columns (city and street).
EDIT: Also you cannot use the #Query annotation that much dinamically (you van insert parameter values dinamically, but not parameters. That's where Specificaion is handy)
EDIT2: I know this is not the 2.x.x Spring version, but 1.5.x, but the idea is the same for joins.
Hello.
It may seem that this question is made before but I cannot see any answer in internet to this concrete case.
This is my starting point:
HQL joins repeats records on Hibernate 5.2/Spring 4.3. This is a simple Many to One/One to many mapping: I have my Root Entity (Usuarios) and my Child entity (Perfiles).
I tried the simple example with Oracle 11, and MySQL 5, and it always repeats the root Entity results. I dont if it has to be taht way... I doubt it (I am not new to Hibernate)
The configuration is OK I already revidsed it a bunch of times.
The problem with this simple HQL, is that it repeats the object Usuarios for each one of its children (Perfiles), so if I have one Usuario with 3 Perfiles, it appears 3 times.
No left joins, no EAGER fetching, Usuarios is mapping to a Set of Perfiles. Everything is alrigth!!!!! I cant believe it is repeating the records. Of course if i put a distinct there, I get one simple object Usuarios (thats what i want of course), perfectly populated with its sets and subObjetc.. like Perfiles. But i Cannot find why it is acting that way, something must be wrong if I have to write the distinc in the HQL.
Here are my two objects:
<pre><code>
select u from Usuarios u
inner join u.perfiles p
#Entity
#Table(name = "usuarios")
public class Usuarios implements java.io.Serializable {
private int usuarioId;
private TcTiposDocumentos tcTiposDocumentos;
private Usuarios usuarioAlta;
private Usuarios usuarioUltModif;
private Usuarios usuarioBaja;
private String nombreApellidos;
private String email;
private String numTelefono;
private Set<Perfiles> perfiles = new HashSet<Perfiles>(0);
public Usuarios() {
}
#Id
#Column(name = "USUARIO_ID", unique = true, nullable = false)
public int getUsuarioId() {
return this.usuarioId;
}
public void setUsuarioId(int usuarioId) {
this.usuarioId = usuarioId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COD_TIPO_DOCUMENTO", nullable = false)
public TcTiposDocumentos getTcTiposDocumentos() {
return this.tcTiposDocumentos;
}
public void setTcTiposDocumentos(TcTiposDocumentos tcTiposDocumentos) {
this.tcTiposDocumentos = tcTiposDocumentos;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COD_USUARIO_BAJA")
public Usuarios getUsuarioBaja() {
return this.usuarioBaja;
}
public void setUsuarioBaja(Usuarios ub) {
this.usuarioBaja = ub;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "usuarios")
public Set<Perfiles> getPerfiles() {
return this.perfiles;
}
public void setPerfiles(Set<Perfiles> perfileses) {
this.perfiles = perfileses;
}
}
//CHILD ENTITY
Entity
#Table(name = "perfiles")
public class Perfiles implements java.io.Serializable {
private int perfilId;
private ComunidadesRegante comunidadesRegante;
private TcRoles tcRoles;
private Usuarios usuarios;
public Perfiles() {
}
public Perfiles(int perfilId, TcRoles tcRoles, Usuarios usuarios) {
this.perfilId = perfilId;
this.tcRoles = tcRoles;
this.usuarios = usuarios;
}
public Perfiles(int perfilId, ComunidadesRegante comunidadesRegante, TcRoles
tcRoles, Usuarios usuarios) {
this.perfilId = perfilId;
this.comunidadesRegante = comunidadesRegante;
this.tcRoles = tcRoles;
this.usuarios = usuarios;
}
#Id
#Column(name = "PERFIL_ID", unique = true, nullable = false)
public int getPerfilId() {
return this.perfilId;
}
public void setPerfilId(int perfilId) {
this.perfilId = perfilId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "COD_USUARIO", nullable = false)
public Usuarios getUsuarios() {
return this.usuarios;
}
public void setUsuarios(Usuarios usuarios) {
this.usuarios = usuarios;
}
}
</code></pre>
I guess thats the way Hibernate 5 works. If you are using HQL, you'll have to use the "distinct" keyword in all the querys in order to get not repeated results (it works OK, no major problems happened).
Hi everyone,
I am new to hibernate JPA. Below is the relationship I defined between Entities A and B.
Here's the code for class A
class A{
#Id
#GeneratedValue
private Long id;
#Column(name = "col_1")
private Long col1;
#Column(name = "col_2")
private Long col2;
#OneToMany(fetch = FetchType.EAGER,mappedBy = "a")
private List<B> bList= new LinkedList<B>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<B> getBList() {
return bList;
}
public void setBList(List<B> bList) {
this.bList = bList;
}
}
And here's the code for class B
class B{
#Id
#GeneratedValue
private Long id;
#NotNull
#ManyToOne(optional=false)
#JoinColumns(value = { #JoinColumn(name = "col_1", referencedColumnName="col_1"),
#JoinColumn(name = "col_1", referencedColumnName="col_2") })
private A a;
#Column(name = "col_1")
private Long col1;
#Column(name = "col_2")
private Long col2;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
I have a crudrepository interface for A. When I run a crud method which loads A entity, I see one left outer join query for each record present in B mapped by col_1 and col_2 columns of A. All these queries are redundant. I am expecting only one left outer join query to be executed. This is causing timeouts in my application. Thanks for you help :)