I know in spring boot that creating a find method in the repository will be base on the variable name of the entity. How can I create a find method in the repository if the variable in my entity is in a camel case. This is what I created is this right?
class Person {
private int id;
private Teacher teacher; //other variable...getters and setters }
private BranchManager branchManager; //other variable...getters and setters }
class Teacher {
private String firstName ;
private String lastName; //getters and setters }
class BranchManager {
private String firstName ;
private String lastName; //getters and setters }
in my repository
#Repository
public interface PersonRepository extends CrudRepository<Person, Long>{
public Person findPersonByTeacherFirstName(String firstName);
//how to find Person by BranchManager firstName and lastName
}
First of all, you have not declared Teacher object in Person. Assuming that you will declare as follows
private Teacher teacher; // name of this object is important to create the repository methods
Try
List<Person> findByTeacher_FirstName(String name);
Note that it findBy returns a List.
If you want to return only the first record. You can use the following
Person findFirstByTeacher_FirstName(String name);
Also, Personally I prefer
Optional<Person> findFirstByTeacher_FirstName(String name);
as this will return Optional.empty() in case it couldn't find a record. Also, it will force you to check for its presence, thereby, avoiding NullPointerException.
Update 1
As requested in comment,
If you want to you want to find by firstName and lastName, you can use the following
Person findFirstByTeacher_FirstNameAndTeacher_LastName(String firstName, String lastName);
You can refer to this link for these conventions.
Update 2
Search by branchManager's firstName and lastName
Person findFirstByBranchManager_FirstNameAndBranchManager_LastName(String firstName, String lastName);
Update 3
As mentioned by Aluan Haddad in comments, this practice is not scalable.
As an option, I am giving an alternative way to hit queries to DB.
You can create an example object and pass it to findAll(Example e) implementation of JpaRepository.
final Teacher exampleTeacher = new Teacher();
exampleTeacher.setName("Teacher Name");
final Person examplePerson = new Person();
example.setTeacher(exampleTeacher);
Now pass this examplePerson to Repository using
List<Person> findAll(Example.of(examplePerson));
This will return all the persons whose teacher's name is Teacher Name.
And as an alternative to C# EntityFramework, you can use QueryDSL in Java.
Related
So I have a student class
#Table(name = "student")
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String idnumber;
private String adress;
private boolean active=Boolean.TRUE;
#OneToMany( mappedBy="student")
private Set<Contact> contacts = new HashSet<>();
#ManyToMany(mappedBy = "students")
private Set<Team> teams = new HashSet<>();
}
and I have repository
public interface StudentRepository extends JpaRepository<Student,Long> {
}
I want to create customrepository to find student idnumber, and while creating new student it will check all students if idnumber already exists throw exception.
You can use the jpa repository method
studentRepo.findById(studentId)
To fetch a record in the database with the studentId.
If you want it to find the record exists or not you can make the method return null and check whether the object is null or not and throw the appropriate exception. Like in the below example.
Student student = studentRepo.findById(studentId).orElse(null);
if(student == null)
throw new CustomException("student does not exist");
No need to create a new repo as customrepository to implement custom query. You can write a new method to retrieve data.
public interface StudentRepository extends JpaRepository<Student, Long> {
// using JPA method
Optional<Student> findByIdnumber(String idNumber);
// using JPA query | this is an optional method in case you need to implement any query in future
// ?1 represents the first parameter of the method, which is 'idNumber'
#Query(value = "SELECT s.id FROM Student s WHERE s.idnumber=?1", nativeQuery = true)
String findIdByIdnumber(String idNumber);
}
Through the service you can check whether a record is available or not and perform a respective action (throw an exception).
I have a Student class.
#Entity
public class Student {
private long id;
private String name;
private String department;
private String subDepartment;
private int marks;
//getters, setters and constructors.
}
I want to be able to get min, max and average marks grouped by department and then subDepartment.
My repository:
public class StudentRepository extends JpaRepository<Student,Long> {
#Query("Select s.department, s.subDepartment , min(marks), max(marks), avg(marks) from Student as s groupby s.department, s.subDepartment")
List<Object[]> getStatsByDepartmentAndSubDepartment()
}
Of course, this works but I want to avoid using List<Object[]> and instead get something like:
Map<String, Map<String,Stats>> class
Where first key is Department, second key is subDepartment and Stats class encapsulates min,max,average
Is there a way to do this with Spring JPA. ? I checked and nested classes are not possible.
How about Hibernate or any other solution? I am a newbie when it comes to Hibernate.
I am using Spring boot and can add any dependency needed. Any database is ok, even H2 for a start.
You cannot return a map.
You can convert the rows using a class DTO.
Class DTO with constructor
You can also create a class DTO:
class StudentDTO {
public StudentDTO(String dep, String subDep, Integer min, Integer max, Double avg ) {
this.stats = new Stats(min, max, avg);
...
}
}
And then call it in the select clause:
select new StudentDTO(s.department, s.subDepartment, avg(s.marks), ...) from ...
This will return a List<StudentDTO>.
As long as the constructor matches the select clause, it will work fine.
Interface DTO
You can also define DTO using interfaces, but I don't think it works if you have nested classes.
This will work though:
interface StudentViev {
String getDepartment();
String getSubdepartment();
Integer getMinMark();
Integer getMaxMark();
Double getAvgMark();
}
and now you can use it as type for the list:
#Query("Select s.department as department, s.subDepartment as subDepartment, min(marks) as minMark, max(marks) as maxMark, avg(marks) as avgMark from Student as s groupby s.department, s.subDepartment")
List<StudentView> getStatsByDepartmentAndSubDepartment()
Note that I've used aliases in the select cluase so that the column names returned match the getters in the interface
I have an interface which is extending crud repository
public interface PersonRepo extends CrudRepository<Person, String> {
#Query(value="select name from PERSON where addr=?1", nativeQuery = true)
List<Person> getPeronUsingAddress(String addr);
}
Person entity looks like this:
class Person {
private String name;
private String phoneNumber;
private String address;
//along with getters setters and all basic hibernate annotation to persist and retrieve
}
the person object is saved into the databse and at the time of retrieving the native query is working fine as hibernate executes correct query. But I am not able to get the return type.
If the return type is List of Person then I am getting InvalidDataAccessResourceUsageException
If I create an interface and use the list of interface as return type like
interface response {
String getName();
}
List of Response interface getPeronUsingAddress(String addr);
then I am getting proxy object in the service. I am not able to get the datas from proxy object.
Another approach I did is to use List of object as return type. But it is not possible to downcast to my Person Object.
How to do that.? Or is there any other solution by which I can return selective columns from crud repository and get a Java object with those selected Columns.
In order to fetch selected columns from an entity, you can do like below :
class Person {
private Integer id;
private String name;
private String phoneNumber;
private String address;
//along with getters setters and all basic hibernate annotation to persist and retrieve
}
Create a DTO or Java Object like below :
public class PersonDTO {
private Integer id;
private String name;
private String phoneNumber;
private String address;
public PersonDTO(Integer id, String name, String phoneNumber, String address) {
// logic here
}
//If you want just want name and phone number.
public PersonDTO(String name, String phoneNumber) {
// logic here
}
// you can't create overridden constructors as all members are of same type and at runtime program won't be able to differentiate unless you provide some logic for it.
// getters, setters, any other methods here...
}
Now below will be you Query but it's not native, if you want to keep native query then you will need to use ResultTransformer like here
#Query("select new your.package.PersonDTO(p.name, p.phoneNumber) from Person p where p.id = :id")
public PersonDTO getPersonById(Integer id);
Below code is for demo purpose only.
My Entity bean looks like this
#Entity
class Employee {
#EmbeddedId
private EmployeeKey employeeKey;
private String firstName;
private String lastName;
// Other fields
// Getter and Setters
}
The Embeddable class:
#Embeddable
class EmployeeKey implements Serializable {
private int employeeId;
private String branchName;
private String departmentName;
//Getter and Setters
}
I can write JPARepository interface method to find Employees by the EmbeddedId that returns me results as well.
interface EmployeeRepository extends JpaRepository<Employee, EmployeeKey> {
List<Employee> findByEmployeeKey(EmployeeKey employeeKey);
}
Question:
Suppose, while querying I have employeeId and branchName only, and I don't want to put filter on departmentName
In such cases how can I write my Repository method
Does JPA have something in-build for such scenario?
List<Employee> findByEmployeeKeyEmployeeIdAndEmployeeKeyBranchName(
int employId,
String branchName);
Should work Have a look at query derivation
Here is how it worked for me.
#Ketrox's answer is absolutely correct and works fine. But in my real scenario I had 6 fields to search by and which resulted in an 120+ characters long method name. (Something like below)
List<Employee> findByEmployeeKeyField1AndEmployeeKeyField2AndEmployeeKeyField3AndEmployeeKeyField4AndEmployeeKeyField5AndEmployeeKeyField6(String field1, String field2, String field3, String field4, String field5, String field6);
Which is certainly not good enough to read and more than good enough to make codenarc unhappy.
Finally I used find by example and that turned out to be really pleasant solution.
Repository:
//skipped lines
import org.springframework.data.domain.Example
//skipped lines
interface EmployeeRepository extends JpaRepository<Employee, EmployeeKey>{
List<Employee> findAll(Example<Employee> employee);
}
Usage:
// Prepare Employee key with all available search by keys (6 in my case)
EmplyeeKey key = new EmplyeeKey();
key.setField1("field1_value");
key.setField2("field2_value");
//Setting remaining 4 fields
// Create new Employee ans set the search key
Employee employee = new Employee();
employee.setEmployeeKey(key);
// Call the findAll by passing an Example of above Employee object
List<Employee> result = employeeRepository.findAll(Example.of(employee));
I have elaborated the search by Spring Data JPA find by #EmbeddedId Partially
First of all, please forgive my ignorance in both Java and Hibernate, I'm studying different ORM solutions and am not a Java programmer.
1) Is it possible to map the following class hierarchy to a database table, with Person.name and Employee.name pointing to different columns?
abstract public class Person {
private String name;
}
public class Employee extends Person {
private String name;
}
2) Supposing that the answer to 1) is Yes, is there a way to create a HQL or Criteria query which would ask Hibernate to return Employee objects, with a criterion on Person.name?
Something like that:
SELECT e FROM Employee e WHERE e.super.name = "test";
1) Yes. This can be accomplished via a MappedSuperclass, and annotating your columns
#MappedSuperclass
abstract public class Person {
#Column(name="PERSON_NAME")
private String name;
}
#Entity
public class Employee extends Person {
#Column(name="EMPLOYEE_NAME")
private String name;
}
2) No. Not without changing the attribute names of one of either Person or Employee. You could however query for all person objects with that name and cast the results to Employees. Another option would be to use Native SQL to query the column you want, again probably not ideal.
SELECT p FROM Person p WHERE p.name = "test";