Jpa findAllBy* using Long id instead of an entity - spring

I just do not want to do:
myEntity = findById(Long id)
findAllByEntityAnd*(MyEntity myEntity, *)
instead I want do:
findAllByEntityAnd*(Long entityId, *)
Is there something I missed to achieve what I wanted or I just cannot achieve it directly?
Thanks for the help ~
When I tried it, the Spring prompted me:
java.lang.IllegalArgumentException: Could not create query metamodel for method public abstract java.util.List com.worksap.morphling.raptor.dump.thread.dao.ThreadDoRepository.findAllByDumpDoAndLocksWaitingContains(java.lang.Long,java.lang.String)!
Here is my table for your reference:
#Data
#Builder
#Entity
#Table(name = "thread_info")
#AllArgsConstructor
#NoArgsConstructor
public class ThreadDo implements Serializable {
private static final long serialVersionUID = -1L;
String name;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "thread_dump_id")
#JsonIgnore
private ThreadDumpDo dumpDo;
...
}
#Data
#Builder
#Entity
#Table(name = "thread_dump")
#AllArgsConstructor
#NoArgsConstructor
public class ThreadDumpDo implements Serializable {
private static final long serialVersionUID = -1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
mappedBy = "dumpDo",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<ThreadDo> threadDoList;
}
And then I have a ThreadDoRepository and want to locate the threadDos directly like this:
List<ThreadDo> findAllByDumpDoAndLocksWaitingContains(Long dumpId, String lockWaiting);
I can achieve it via #Query as follows, but I really think it's pretty ugly since it's easy to interpret (I think).
#Query(value = "select * from thread_info t where t.thread_dump_id = ?1 and t.locks_waiting like ?2",
nativeQuery = true)
List<ThreadDo> findAllByDumpDoAndLocksWaitingContains(Long dumpId, String lockWaiting);

Try this
List<ThreadDo> findAllByDumpDo_IdAndLocksWaitingContains(Long dumpId, String lockWaiting);

Related

How to implements entity with 2 entity as primary key with jpa annotation and repository

i want to implement a many to many association with quantity information in it . like this :
#Entity
#Table(name = "reserves")
#Getter #Setter #NoArgsConstructor
public class Reserve {
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin bloodGroup;
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Banque banque;
private int quantity;
}
the GroupSanguin and the Banque are two class stored in the database two . here is the code for the two if you need :
#Entity
#Table(name = "groupe_sanguins")
public class GroupeSanguin {
#Id
private String groupe;
#OneToMany(mappedBy = "groupeSanguin")
private List<Donneur> donneurs;
}
#Entity #Getter #Setter #NoArgsConstructor
public class Banque {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true,nullable = false)
private String nom;
private String adresse;
#Column(unique = true)
private String telephone;
private String localisation;
}
so my i want to know how to annotate the JpaRepository to take the two as primary key like this and is my annotation good for it to work ?
public interface ReserveRepository extends JpaRepository<
Reserve,
//what to put here ?
>
This isn't a JPA question in fact, it's a relationnal database conception.
If Reserve has is own data and links with other entity it has it own Id
You can add unicity constraint
#Entity
#Table(name = "reserves", uniqueConstraints={
#UniqueConstraint(columnNames = {"banque_id", "groupe_id"})
#Getter #Setter #NoArgsConstructor
public class Reserve {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin bloodGroup;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "banque_id")
private Banque banque;
private int quantity;
}
i've found this solutions too.
#Entity
#Table(name = "reserves")
#Getter #Setter #NoArgsConstructor
#IdClass(ReserveId.class) //this annotation will tell that id that the
// the id will be represented by a class
public class Reserve {
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "groupe_id")
private GroupeSanguin groupeSanguin;
#Id
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "banque_id")
private Banque banque;
private int quantity;
}
and the id class should implements Serializable like this :
#Getter #Setter
public class ReserveId implements Serializable {
private Banque banque;
private GroupeSanguin groupeSanguin;
}
and finally the repository will be like that :
#Repository
public interface ReserveRepo extends JpaRepository<Reserve, ReserveId>{}
See your Reserve class has nowhere mentioned composite primary key. First you need to fix the model, You can refer to the solution here How to create and handle composite primary key in JPA

LazyInitializationException when get EAGER fetch object OneToOne

I have two entity
#Entity
#Table(name = "user")
#Data
#Builder
#EqualsAndHashCode(callSuper=false)
#ToString
#AllArgsConstructor
#NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "name")
private String name;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn
private UserLastLogin userLastLogin;
}
#Entity
#Table(name = "lastLogin")
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#ToString(onlyExplicitlyIncluded = true)
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class UserLastLogin implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "name")
private String userName;
#Column(name = "date")
private LocalDateTime date;
#OneToOne
#MapsId
#JoinColumn(name = "name")
private User user;
}
I use spring boot with spring data and jpa, hibernate in latest version.
In documentation is that #OneToOne is default EAGER, but when i get eager fetch object, i get lazyInitializationException when i not use #Transactional in get method. I don't understant why...
public UserDto getUser(String userName) {
var user= userRepository.getById(userName);
d.getSystemUserLastLogin(); // this throw lazy initialization exception
return mapper.entityToDto(d);
}
When i'will mark this method #Transactioal, this work. But, not recommendend used transactions in get method. I need use EAGER fetch in this relationship.
When i view query hibernate, i have one select, but children object is not available.
Hibernate:
select
user0_.name as nazwa1_4_0_,
user2_.name as name1_23_2_,
user2_.data as data3_23_2_
from
user0_
left outer join
last_login user2_
on user0_.name=user2_.name
where
user0_.name=?
The problem was that despite the fetch eager, lazy was used. This was due to the use of the getById method from the repository, which retrieves only the object's references and snaps all the fields when lazy is retrieved. Changing to findById solves the problem as findById takes an object, not a reference.
I would recommend you to use secondary tables instead like this:
#Entity
#Table(name = "user")
#Data
#Builder
#EqualsAndHashCode(callSuper=false)
#ToString
#AllArgsConstructor
#NoArgsConstructor
#SecondaryTable(name = "lastLogin", pkJoinColumns = #PrimaryKeyJoinColumn(name = "name"))
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "name")
private String name;
#Column(table = "lastLogin", name = "date")
private LocalDateTime date;
}
Also see https://www.baeldung.com/jpa-mapping-single-entity-to-multiple-tables for more details.

How to make composite Foreign Key part of composite Primary Key with Spring Boot - JPA

I have a problem with the historization of objects in the database.
the expected behavior of the save JpaRepository method is : Insert in the two tables idt_h and abo_h
But the current behavior is Insert in the idt_h table and update in the abo_h table.
#Data
#Entity
#Table(name = "ABO_H")
#AllArgsConstructor
#NoArgsConstructor
public class AboOP {
#Id
#Column(name = "ABO_ID")
private String id;
#Column(name = "ABO_STATUT")
private String statut;
#Column(name = "ABO_DATE_STATUT")
private Instant date;
#Column(name = "ABO_CoDE")
private String code;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "IDC_ID", referencedColumnName = "IDC_ID"),
#JoinColumn(name = "DATE_HISTO", referencedColumnName = "DATE_HISTO")
})
private IdtOP idtOP;
}
#Data
#Entity
#Table(name = "IDT_H")
#AllArgsConstructor
#NoArgsConstructor
public class IdtOP {
#AttributeOverrides({
#AttributeOverride(name = "id",
column = #Column(name = "IDC_ID")),
#AttributeOverride(name = "dateHisto",
column = #Column(name = "DATE_HISTO"))
})
#EmbeddedId
private IdGenerique idtId = new IdGenerique();
//Other fields
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Embeddable
public class IdGenerique implements Serializable {
private String id;
private Instant dateHisto;
}
I think that the class IdGenerique which groups the id and dateHisto is not well invoked for the table abo_h ??
thanks in advance
When you use the save() method, entityManager checks if the entity is new or not. If yes, the entity will be saved, if not, it'll be merged
If you implement your Entity Class with the inteface Persistable, you can override the method isNew() and make it returns True. In that case the save() method will persist, and not merge, your entity.

Using JPARepository on Entity with Inheritancetype.TABLE_PER_CLASS result in wrong query

I've defined 2 entities in my project using the inheritance type "TABLE_PER_CLASS".
After that I've defined 2 repositories to access data but, when I use them to find records for the B entity, the query generated does not include a JOIN statement but only "SELECT id, name, alternate_name from b" and it fails because "name" field does not exists.
Did I miss something that I can't see?
Entity A:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "a")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public abstract class A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
...
...
}
Entity B:
#Entity
#Table(name = "b")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class B extends A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "alternate_name")
private String alternateName;
...
...
}
BaseRepository:
#SuppressWarnings("unused")
#NoRepositoryBean
public interface BaseRepository<T extends A> extends JpaRepository<T, Long> {
}
ARepository:
#SuppressWarnings("unused")
#Repository
public interface ARepository extends BaseRepository<A> {
}
BRepository:
#SuppressWarnings("unused")
#Repository
public interface BRepository extends BaseRepository<B> {
}
I assume that you have a table a and a table b.
But TABLE_PER_CLASS will only generate/use a table per concrete subclass. In your case just table b.
If you want table a and b you must use the JOINED strategy.
Try to use #GeneratedValue annotation without explicitly saying the strategy.
#Id
#GeneratedValue
private Long id;

Spring Framework + Spring Data + Hibernate Jpa OneToMany child removal fails

I have an unidirectional OneToMany JPA entity mapping in my (Spring Framework + Spring Data + Hibernate JPA) project. Entity classes are like in the following code.(I have removed irrelevant class members for brevity).
#Entity
#Table(name = "employees")
class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "employee_id")
private List<DepartmentAssignment> departmentAssignments = new ArrayList<>();
}
#Entity
#Table(name = "department_assignments")
class DepartmentAssignment{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#NotNull
#Column(name = "employee_id")
private Integer employeeId;
#NotNull
#Column(name = "department_id")
private Integer departmentId;
}
#Entity
#Table(name = "departments")
class Department{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
}
And, in one of my service classes have a method to remove a DepartmentAssignment from an Employee like below.
public Employee deleteDepartmentAssignment(Integer empId, Integer deptAssignmentId) {
Employee employee = employeeRepository.findOne(empId);
if(employee != null) {
for ( DepartmentAssignment da : employee.getDepartmentAssignments()) {
if(da.getId().equals(deptAssignmentId)) {
employee.getDepartmentAssignments().remove(da);
employee = employeeRepository.save(employee);
break;
}
}
}
return employee;
}
However, calling above methods gives me an error: org.hibernate.exception.ConstraintViolationException ,and in the SQL log, I can see Column 'employee_id' cannot be null error for the last SQL statement of the transaction.
Can anybody tell me what I'm doing wrong here and how to get it fixed?
You don't need to add
#NotNull
#Column(name = "employee_id")
private Integer employeeId;
to the Employee, if you use #JoinColumn(name = "employee_id"). Try to remove it.
You can try the following, not sure why you use the plain id in the object. Thats not object relational mapping.
For more details see Hibernate triggering constraint violations using orphanRemoval
#Entity
#Table(name = "employees")
class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "employee", orphanRemoval = true)
private List<DepartmentAssignment> departmentAssignments = new ArrayList<>();
}
#Entity
#Table(name = "department_assignments")
class DepartmentAssignment{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(optional=false)
private Employee employee;
#ManyToOne(optional=false)
private Department department;
}
#Entity
#Table(name = "departments")
class Department{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
You must look .hbm.xml file and you should mapping your Entity in this file and
you can look this example
http://www.mkyong.com/hibernate/hibernate-one-to-many-relationship-example/
I hope it will be useful for you.
try removing
cascade = CascadeType.ALL
but im not 100% sure..

Resources