How to bind #PathVariable value to #Where clause in Spring boot rest API - spring-boot

I am reading #PathVaraible in Controller and using #Where annotation in Entity. If I pass hardcoded value to #Where clause in Entity it is working fine. But I want to pass run time value which is passed in #PathVariable to #Where in Entity.
#GetMapping("/api/v1/course/details/{courseId}")
public List<CourseDto> getcourseDetails(#Valid #PathVariable final String courseId) {
// lines of code
}
#Entity
#Where(clause="course_id=:courseId")
public class CourseEntity{
#NotBlank(message = "Please provide courseId)
#Getter
#Setter
#Column(columnDefinition = "nvarchar(50)",length = 50)
private String courseId;
}
#Repository
public interface MyRepo extends JpaRepository<Course, Integer> {
}
With this code I am getting error as
Incorrect syntax near ':'.
How to fix this error. Is there any other solution ?

You cannot change a #Where clause at runtime. If you want to set a parameter you can use #Filter:
#Entity
#FilterDef(
name="courseFilter",
parameters=#ParamDef(
name="courseId",
type="int"
)
)
#Filter(
name="firstAccounts",
condition="course_id=:courseId"
)
public class CourseEntity{
...
}
Now with the Hibernate ORM session or entity manager you can set a parameter the following way:
entityManager
.unwrap( Session.class )
.enableFilter( "courseFilter" )
.setParameter( "courseId", ...);
List<CourseEntity> courses = entityManager
.createQuery("from CourseEntity c", CourseEntity.class)
.getResultList();
The Hibernate ORM documentation has more details about filtering entities using #Filter or #Where.

Related

Spring data jpa search filter by foreign key and type

Model Class Vehicle
#Column(name="type",nullable=false)
private String type;
#Column(name="last_service_date",nullable=false)
private String lastServiceDate;
#Column(name="seats",nullable=false)
private Long seats;
#Column(name="bags_capacity",nullable=false)
private Long bagsCapacity;
#Column(name="milage",nullable=false)
private Long milage;
//for Franchise object id
private transient Long fId;
#ManyToOne
#JoinColumn(name="franchise_id")
private Franchise fkFranchiseId;
#Repository
public interface VehicleRepository extends JpaRepository<Vehicle,Long>
{
}
I am using spring data jpa repositories and want to search Vehicle by type and foreignKey=>(zipcode) how can i find
Just add a method in your Vehicle JPA repository interface as follow:
findAllByTypeAndFkFranchiseIdZipCode(String type, String zipCode);
And also you are welcome to check docs of Spring Data Jpa
List<Vehicle> findAllByTypeAndFkFranchiseId_ZipCode(String type, String zipCode);
You can use JPA repo method name query documented here https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
public interface VehicleRepo extends JpaRepository<Vehicle, String> {
List<Vehicle> findAllByTypeAndFkFranchiseIdZipCode((String type, String zipCode);
Page<Vehicle> findAllByTypeAndFkFranchiseIdZipCode((String type, String zipCode,Pageable page);
}
for those who have a more complex object and want to keep their code, u can also use #Query for fetching data.
u just need to do this like this:
#Repository
public interface VehicleRepo extends JpaRepository<Vehicle, String> {
#Query("from Vehicle v where v.type = :type and v.fkFranchise.zipCode = :zipCode")
List<Vehicle> findAllByTypeAndZipCode(String type, String zipCode);
}

Externalize mongo json query using spring boot

I have just started using spring data MongoDb with Spring-Boot.
I have some mongo based json queries added in the interface using #query annotation when using spring data repository.
I want to know if it is possible to externalize or separate out the JSON query outside the codebase so that it can be optimized separately and
also not having it mixed with code.
Thanks for your suggestions.
This is the code which i have added in my interface and annotated with #query annotation.
#Query("{ 'firstname' : ?0 ,'lastname': ?1}")
List findByCriteria(String firstname,String lastname);
The above is a simple example. I have complex conditions involving $and and $or operators too .
What i basically want to achieve is externalize the above native mongo json query to a config file and refer that in the above annotation.
Spring data supports something similar when using jpa with hibernate. But not sure if we can do the same using spring data mongodb with spring boot.
Do like this (I am explaining only for the API)
Suppose you have an Entity user
At the Top there will be User domain
public class User extends CoreDomain {
private static final long serialVersionUID = -4292195532570879677L;
#Length(min = 2)
private String name;
#Length(min = 2)
#UniqueUserName(message = "User name already registered,Please choose something Different")
private String userName;
#Length(min = 6)
private String password;
}
User Controller
User Service (Interface)
User ServiceImpl(Service Implementation)
Mongo Repository(Since, I have MongoDb)
Now in userController you will take all the queries , Param(Parameters) , Pagerequest like this
public class UserController extends CoreController {
#Autowired
private UserService userService;
/*
* This controller is for getting the UserDetails on passing the UserId in
* the #param Annotation
*/
#GET
#Path("{id}")
public User getUser(#PathParam("id") String UserId) {
User user = new User();
user = userService.findUserId(UserId);
if (user == null)
throw new NotFoundException();
log.info("The userId you searched is having the details as :" + user);
return user;
}}
For serviceInterface you will have :
public interface UserService {
// Boolean authenticateUser(User user);
User findUserId(String UserId);
}
For serviceImpl :
public class UserServiceImpl implements UserService {
#Setter
#Autowired
private UserRepository userRepository;
/*
* This method will find user on the basis of their userIds passed in the
* parameter.
*/
#Override
public User findUserId(String UserId) {
User userIdResult = userRepository.findOne(UserId);
log.info("The userDetail is" + userIdResult);
return userIdResult;
}
In mongoRepository for user we will have:
A default query findById(String userId);
Hopefully this will help you.

Spring data query where column is null

Suppose I have entities (getters/setters and various details omitted for brevity):
#Entity
class Customer{
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "customer")
Collection<Coupon> coupons;
}
#Entity
class Coupon{
...
#Temporal(value = TemporalType.TIMESTAMP)
private Date usedOn;
#ManyToOne(fetch = FetchType.LAZY)
#NotNull
Customer customer;
}
I wish retrieve all Coupons for a given Customer having null usedOn.
I,'ve unsuccessfully defined a method in the CouponRepository as described in docs
#Repository
public interface CouponRepository extends CrudRepository<Coupon, Long> {
Collection<Coupon> findByCustomerAndUsedOnIsNull(Customer);
}
but this leads on a compiler error Syntax error, insert "... VariableDeclaratorId" to complete FormalParameterList.
My fault, the correct definition is
#Repository
public interface CouponRepository extends CrudRepository<Coupon, Long> {
Collection<Coupon> findByCustomerAndUsedOnIsNull(Customer customer);
}
I simply missed the parameter name :-(
You can use IsNull to check null columns in JPA query.
For example for any columnA you can write query like query like
findByColumnAIsNull
In this case you can write queries like
#Repository
public interface CouponRepository extends CrudRepository<Coupon, Long> {
Collection<Coupon> findByCustomerAndUsedOnIsNull(Customer customer);
List<Coupon> findByUsedOnIsNull();
}
Also you can check how this queries will be
Refer this Spring Data JPA Query creation this will help you lot to understand and create different type of JPA query variation.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
Try changing your method to this (assuming Customer.id is a long):
Collection<Coupon> findByCustomer_IdAndUsedOnIsNull(Long customerId);
then use like this:
repo.findByCustomer_IdAndUsedOnIsNull(customer.getId());

Proper Way to layer Spring JPA based DAO using Spring Boot Framework

Am new to Spring Boot & JPA...
Let's say I have two entities mapped to two tables which are joined in a database.
Student-1------<-Course
Also, lets presume that the database is already created and populated.
This depicts that one student has many courses...
My Student Entity:
#Entity
public class Student {
#OneToMany(mappedBy="student")
private List<Courses> courses;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Student_Id")
private long studentId;
#Column(name = "Student_Name")
private String studentName;
protected Student() { }
// Getters & Setters
}
My Course Entity:
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Course_Id")
private long courseId;
#Id
#Column(name = "Student_Id")
private long studentId;
#ManyToOne
#PrimaryKeyJoinColumn(name="Student_Id", referencedColumnName="Student_Id")
private Student student;
#Column(name = "Course_Name")
private String courseName;
// Getters & Setters
}
In Spring Boot's Tutorial Guides, it illustrates how to extend a CrudRepository interface, but
it doesn't specify how to setup a Spring based DAO which contains custom finder methods which use HQL and EntityManager inside it.
Is the following DAO and DaoImpl correct?
public interface CourseDao {
List<Course> findCoursesByStudentName(String studentName);
}
#Repository
public class CourseDaoImpl implements CourseDao {
#PersistenceContext
EntityManager em;
public List<Course> findCoursesByStudentName(String studentName) {
String sql = "select c.courseName" +
"from Course c, Student s " +
"where c.course_id = s.student_id " +
"and s.studentName = :studentName ";
Query query = em.createQuery(sql);
query.setParameter("studentName", studentName);
return query.getResultList();
}
}
And then in the client code, for example, in the main class:
public class Application {
#Autowired
CustomerDao dao;
public static void main (String args []) {
List<Course> courses = dao.findCoursesByStudentName("John");
}
}
Is this the standard way to use HQL inside Spring DAOs ? I've seend examples of the #Transactional annotation being prepended to the DAO class's impl (e.g. CustomerDAOImpl) ?
Please let me know if this is the write way to structure my Spring Boot app or am I supposed to extend / add to the CrudRepository only?
If someone could correct my example and point me to a URL which talks about HQL using Entities that are joined, I would be very grateful.
The Spring Boot guides didn't depict joins or DAOs - I just need to learn how to properly create finder methods which emulate select statement which return lists or data structures.
Thanks for taking the time to read this...
If I understood your question correct you do have two questions:
How to create a DAO and DAOImpl?
Where to put your Transaction annotations?
In regards to the first question I want to point out that this is a question in regards to spring-data-jpa using Hibernate as a JPA provider, not spring-boot.
Using Spring Data I usually skip completely to create a DAO but directly use a Custom Repository extending a standard one like CrudRepository. So in your case you don't even have to write more code than:
#Repository
public interface StudentRepository extends CrudRepository<Student, Long> {
List<Student> findByStudentName(String studentName);
}
Which will be sufficient and Spring Data will take care of filling it with the correct implementation if you use
#Autowired
StudentRepository studentRepo;
in your service class. This is where I also usually annotate my methods with #Transactional to make sure that everything is working as expected.
In regards to your question about HQL please look into the spring data jpa documentation, which points out that for most of the cases it should be sufficient to stick to proper named methods in the interface or go for named queries (section 3.3.3) or use the #Query annotation (section 3.3.4) to manually define the query, e.g. should work (didn't tried):
#Repository
public interface #CourseRepository extends CrudRepository<Course, Long> {
#Query("select c.courseName from Course c, Student s where c.course_id = s.student_id and s.studentName = :studentName")
public List<Course> findCoursesByStudentName(String studentName);
}
If you annotate your CourseDaoImpl with #Transactional (Assuming your have defined JpaTransactionManager correctly) You can just retrieve the Student with the matching name and call the getCourses() method to lazy load the Courses attached to that student. Since findCoursesByStudentName will run within a Transaction it will load the courses just fine.
#Repository
#Transactional(readOnly=true)
public class CourseDaoImpl implements CourseDao {
#PersistenceContext
EntityManager em;
public List<Course> findCoursesByStudentName(String studentName) {
String sql = "select s " +
"from Student s " +
"where s.studentName = :studentName ";
Query query = em.createQuery(sql);
query.setParameter("studentName", studentName);
User user = query.getSingleResult();
if(user != null) {
return user.getCourses();
}
return new ArrayList<Course>();
}
}

Hibernate and JPA always load referenced tables

I am working with Hibernate 4+ Spring MVC + Spring Data with JPA annotations:
#Entity
public class ClassOne implements Serializable{
......
#OneToMany(mappedBy = "mapper", fetch=FetchType.LAZY)
private Set<ClassTwo> element = new HashSet<ClassTwo>(0);
//more fields
//getters and setters
//equals and hashcode
}
#Entity
public class ClassTwo implements Serializable{
......
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "CEN_CEN_CODIGO", nullable = false)
private ClassOne classOne;
//more fields
//getters and setters
//equals and hashcode
}
public interface ClassOneRepository extends CrudRepository<ClassOne, Long> {
#Override
#Query("select c from ClassOne c")
public List<ClassOne> findAll();
}
#Service
public class ClassOneService {
#Autowired
private ClassOneRepository classOneRepository;
#Transactional(readOnly=true)
public List<ClassOne> findAll() {
return classOneRepository.findAll();
}
}
And finally I call thie service from my #Controller
#Autowired
ClassOneService classOneService;
I expect results ONLY from ClassOne but retrieving the JOIN values with ClassTwo and all the database tree associate. Is it possible to get only values for ONE table using this schema? Is it a cache problem or Fetching not LAZY?
EDIT: I added the relatioship between two classes
Thank you
You must have the following anotation above your Set<ClassTwo> or its getter:
#OneToMany(fetch = FetchType.LAZY, ...)
See http://docs.oracle.com/javaee/7/api/javax/persistence/OneToMany.html#fetch()
It seems to be that simple "SELECT *" JPA query returns all eagerly fetched fields for the entity.
Please refer to: JPA - Force Lazy loading for only a given query
and http://forcedotcom.github.io/java-sdk/jpa-queries.
So my solution would be to use SessionFactory to get current session and then use "classic" method
return getCurrentSession().createCriteria(persistentClass).list();
Another possible way is to create let's say a POJO object without Set which uses the same table as ClassOne.
I've just added #Lazy for each #ManyToOne and #OneToMany relationship. It seems that Spring Data needs Spring annotations but I supposed that just was necessary to add fetch = FetchType.LAZY. No more Eager behaviours ;).
Thanks for your responses

Resources