I have a spring JPA Application. I am using Spring Boot with Spring JPA native query to fetch the data but I am not getting any results. Database used is Oracle 11g. Can anyone please help me to fix it ?
Here is the code.
Repository
public interface RoseRepository extends JpaRepository {
#Query(value = "SELECT RWM.RIG_WELL_ID as id,RM.RIG_NAME as name FROM RIG_WELL_MASTER RWM JOIN RIG_UNIT_MAPPER RUM ON RWM.fk_rig_master_id = RUM.fk_rig_master_id JOIN RIG_UNIT RU ON ru.rig_unit_code = RUM.UNIT_CODE JOIN RIG_MASTER RM ON RM.RIG_MASTER_ID = RWM.FK_RIG_MASTER_ID AND RWM.START_TIME=TO_DATE(?1,'DD-MON-YY') AND RU.UNIT_NAME=?2", nativeQuery = true)
public List<RigDetails> fetchRigsForAUnit(String startTime,String unitName);
}
interface
public interface RigDetails {
String getId();
String getName();
}
Service
#Service
public class RoseService {
#Autowired
private RoseRepository roseRepository;
public List<RigDetails> fetchRigsForAUnit(String startTime, String unitName) {
List<RigDetails> rigList = null;
try{
rigList = roseRepository.fetchRigsForAUnit(startTime, unitName);
}catch(Exception e){
e.printStackTrace();
}
return rigList;
}
}
Related
Following this example I am trying to create custom queries, using the reactive mongo template.
I have defined the following files
Repository definition
#Repository
public interface SimulationRepository extends ReactiveMongoRepository<Simulation, String>, PersSimRepo {
#Query("{userId : ?0}")
public Flux<Simulation> findByUserId(String userId);
#Query("{userId : ?0}")
public Flux<Simulation> findByUserId(String userId, Pageable pageable);
public Mono<Long> countByUserId(String userId);
}
Custom query interface
public interface PersSimRepo {
Flux<Simulation> aaa(String userId, Sort sort, List<GlobalSimStatus> status);
}
Custom query implementation
#Slf4j
public class PerSimRepoImpl implements PersSimRepo {
private final ReactiveMongoTemplate mongoTemplate;
public PerSimRepoImpl(ReactiveMongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
#Override
public Flux<Simulation> aaa(String userId, Sort sort, List<GlobalSimStatus> status) {
log.info("status:{}", status);
Query query = new Query();
query.addCriteria(Criteria.where("userId").is(userId).and("status").in(status));
return mongoTemplate.find(query.with(sort), Simulation.class);
}
}
When I try to start the project I get this exception
Could not create query for public abstract reactor.core.publisher.Flux it.reply.evtb.repository.specialization.PersSimRepo.aaa(java.lang.String,org.springframework.data.domain.Sort,java.util.List); Reason: No property 'aaa' found for type 'Simulation'; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property 'aaa' found for type 'Simulation'
Where Simulation is the object, apparently spring looks for a "aaa" field inside Simulation
This looks like a bug to me, I can't figure out where I am wrong, I use spring boot 2.7.5
#Repository
public class JobSheetDaoImpl implements JobSheetDao {
#Autowired
private HibernateTemplate hibernateTemplate;
#Transactional
public void createJobSheet(JobSheet jobSheet) {
//to get technician who have minimum workload
String query="select * from Technician Where expertiseCompany=? "
+ "AND expertiseDevice=? AND totalJobs=(select MIN(totalJobs) "
+ "from Technician)";
//how to fire this query using hibernatetemplate ???
}
}
You must use the Hibernate Session like this:
SQLQuery sqlQuery = hibernateTemplate.getSession().createSQLQuery(query);
sqlQuery.setParamer(...) // set the parameters you need
List result = sqlQuery.list();
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.
I have a very complicated model. Entity has a lot relationship and so on.
I try to use Spring Data JPA and I prepared a repository.
but when I invoke a method findAll() with specification for the object a have a performance issue because objects are very big. I know that because when I invoke a method like this:
#Query(value = "select id, name from Customer ")
List<Object[]> myFindCustomerIds();
I didn't have any problems with performance.
But when I invoke
List<Customer> findAll();
I had a big problem with performance.
The problem is that I need to invoke findAll method with Specifications for Customer that is why I cannot use method which returns a list of arrays of objects.
How to write a method to finding all customers with specifications for Customer entity but which returns only an IDs.
like this:
List<Long> findAll(Specification<Customer> spec);
I cannot use in this case pagination.
Please help.
Why not using the #Query annotation?
#Query("select p.id from #{#entityName} p")
List<Long> getAllIds();
The only disadvantage I see is when the attribute id changes, but since this is a very common name and unlikely to change (id = primary key), this should be ok.
This is now supported by Spring Data using Projections:
interface SparseCustomer {
String getId();
String getName();
}
Than in your Customer repository
List<SparseCustomer> findAll(Specification<Customer> spec);
EDIT:
As noted by Radouane ROUFID Projections with Specifications currently doesn't work beacuse of bug.
But you can use specification-with-projection library which workarounds this Spring Data Jpa deficiency.
I solved the problem.
(As a result we will have a sparse Customer object only with id and name)
Define their own repository:
public interface SparseCustomerRepository {
List<Customer> findAllWithNameOnly(Specification<Customer> spec);
}
And an implementation (remember about suffix - Impl as default)
#Service
public class SparseCustomerRepositoryImpl implements SparseCustomerRepository {
private final EntityManager entityManager;
#Autowired
public SparseCustomerRepositoryImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public List<Customer> findAllWithNameOnly(Specification<Customer> spec) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> tupleQuery = criteriaBuilder.createTupleQuery();
Root<Customer> root = tupleQuery.from(Customer.class);
tupleQuery.multiselect(getSelection(root, Customer_.id),
getSelection(root, Customer_.name));
if (spec != null) {
tupleQuery.where(spec.toPredicate(root, tupleQuery, criteriaBuilder));
}
List<Tuple> CustomerNames = entityManager.createQuery(tupleQuery).getResultList();
return createEntitiesFromTuples(CustomerNames);
}
private Selection<?> getSelection(Root<Customer> root,
SingularAttribute<Customer, ?> attribute) {
return root.get(attribute).alias(attribute.getName());
}
private List<Customer> createEntitiesFromTuples(List<Tuple> CustomerNames) {
List<Customer> customers = new ArrayList<>();
for (Tuple customer : CustomerNames) {
Customer c = new Customer();
c.setId(customer.get(Customer_.id.getName(), Long.class));
c.setName(customer.get(Customer_.name.getName(), String.class));
c.add(customer);
}
return customers;
}
}
Unfortunately Projections does not work with specifications. JpaSpecificationExecutor return only a List typed with the aggregated root managed by the repository ( List<T> findAll(Specification<T> var1); )
An actual workaround is to use Tuple. Example :
#Override
public <D> D findOne(Projections<DOMAIN> projections, Specification<DOMAIN> specification, SingleTupleMapper<D> tupleMapper) {
Tuple tuple = this.getTupleQuery(projections, specification).getSingleResult();
return tupleMapper.map(tuple);
}
#Override
public <D extends Dto<ID>> List<D> findAll(Projections<DOMAIN> projections, Specification<DOMAIN> specification, TupleMapper<D> tupleMapper) {
List<Tuple> tupleList = this.getTupleQuery(projections, specification).getResultList();
return tupleMapper.map(tupleList);
}
private TypedQuery<Tuple> getTupleQuery(Projections<DOMAIN> projections, Specification<DOMAIN> specification) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = cb.createTupleQuery();
Root<DOMAIN> root = query.from((Class<DOMAIN>) domainClass);
query.multiselect(projections.project(root));
query.where(specification.toPredicate(root, query, cb));
return entityManager.createQuery(query);
}
where Projections is a functional interface for root projection.
#FunctionalInterface
public interface Projections<D> {
List<Selection<?>> project(Root<D> root);
}
SingleTupleMapper and TupleMapper are used to map the TupleQuery result to the Object you want to return.
#FunctionalInterface
public interface SingleTupleMapper<D> {
D map(Tuple tuple);
}
#FunctionalInterface
public interface TupleMapper<D> {
List<D> map(List<Tuple> tuples);
}
Example of use :
Projections<User> userProjections = (root) -> Arrays.asList(
root.get(User_.uid).alias(User_.uid.getName()),
root.get(User_.active).alias(User_.active.getName()),
root.get(User_.userProvider).alias(User_.userProvider.getName()),
root.join(User_.profile).get(Profile_.firstName).alias(Profile_.firstName.getName()),
root.join(User_.profile).get(Profile_.lastName).alias(Profile_.lastName.getName()),
root.join(User_.profile).get(Profile_.picture).alias(Profile_.picture.getName()),
root.join(User_.profile).get(Profile_.gender).alias(Profile_.gender.getName())
);
Specification<User> userSpecification = UserSpecifications.withUid(userUid);
SingleTupleMapper<BasicUserDto> singleMapper = tuple -> {
BasicUserDto basicUserDto = new BasicUserDto();
basicUserDto.setUid(tuple.get(User_.uid.getName(), String.class));
basicUserDto.setActive(tuple.get(User_.active.getName(), Boolean.class));
basicUserDto.setUserProvider(tuple.get(User_.userProvider.getName(), UserProvider.class));
basicUserDto.setFirstName(tuple.get(Profile_.firstName.getName(), String.class));
basicUserDto.setLastName(tuple.get(Profile_.lastName.getName(), String.class));
basicUserDto.setPicture(tuple.get(Profile_.picture.getName(), String.class));
basicUserDto.setGender(tuple.get(Profile_.gender.getName(), Gender.class));
return basicUserDto;
};
BasicUserDto basicUser = findOne(userProjections, userSpecification, singleMapper);
I hope it helps.
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>();
}
}