I wants to mock the view result in junit which is from repository class.
#Modifying
#Query(value = "Select * from v_students", nativeQuery = true)
public String studentsDetails();
Inside view (v_students) have a query, (select name, id, roll from students);
Now I wants to write a test case for this view in junit, by inserting dummy value for name, id, roll. How we can achieve this in JUNIT for view with out manual DB insert.
Related
Hello I wrote inner join between the foreign key and the linked tables, but I can not print the data in the 2nd table. I get an error in Javascript, probably the data in the 2nd table is not coming there. I need user_name in User table but I can not access.
BookController
#Autowired
private BooksService booksService;
#Autowired
private BookCommentsService bookCommentsService;
#RequestMapping(value = "/bookdetail")
public ModelAndView showBookDetailForm(#RequestParam long book_id)
{
ModelAndView mav= new ModelAndView("bookdetail");
List<Books> book=booksService.bookGetWithId(book_id);
List<BookComments> listBookComments= bookCommentsService.listAllDetailComments(book_id);
mav.addObject("listBook",book);
mav.addObject("listBookComments", listBookComments);
return mav;
}
BookCommentsService
#Service
public class BookCommentsService {
#Autowired
private BookCommentsRepository repository;
public List<BookComments> listAllDetailComments(long book_id)
{
return repository.listAllDetailComments(book_id);
}
}
BookCommentsRepository
public interface BookCommentsRepository extends CrudRepository<BookComments, Long> {
#Query(value = "Select b From BookComments b inner join Users u on b.comment_user_id = u.user_id where b.comment_book_id= :book_id")
public List<BookComments> listAllDetailComments(#Param("book_id") long book_id);
}
BookDetail.js
<c:forEach items="${listBookComments}" var="bookcomments">
(No problem here) ${bookcomments.book_comment}
(The error is here) <h4>${bookcomments.user_name}</h4>
</c:forEach>
You should use comment_user_id and close the braces, ${bookcomments.comment_user_id} should work fine.
To solve your problem, you must use another object (known as a Data Transfer Object; DTO) which will contain the details from BookComments and the username.
An example would be:
package com.myapp.book;
public class BookCommentsDto{
private String bookComment;
private String username;
// You can add any other attributes that you need from the table book_comments
// add them in the constructor as well
public BookCommentsDto(String bookComment, String username){
this.bookComment = bookComment;
this.username = username;
}
}
I will refer to the class BookCommentsDto as dto. The above dto is the simple dto you might need. You can add other details you need from book comment in the dto and make sure to add them in the constructor as well. I have included a dummy package name on the top because we need to mention the full classpath of the dto in the query we will write. In the query below, I am creating the dto from the result of the sql. In the query, I am using the constructor of BookCommentsDto to create the object which will be returned.
#Query(value = "Select new com.myapp.book.BookCommentsDto(b.book_comment, u.user_name) From BookComments b inner join Users u on b.comment_user_id = u.user_id where b.comment_book_id= :book_id")
public List<BookCommentsDto> listAllDetailComments(#Param("book_id") long book_id);
Update return type in the service BookCommentsService
#Service
public class BookCommentsService {
#Autowired
private BookCommentsRepository repository;
public List<BookCommentsDto> listAllDetailComments(long book_id)
{
return repository.listAllDetailComments(book_id);
}
}
Update controller
#RequestMapping(value = "/bookdetail")
public ModelAndView showBookDetailForm(#RequestParam long book_id)
{
ModelAndView mav= new ModelAndView("bookdetail");
List<Books> book=booksService.bookGetWithId(book_id);
List<BookCommentsDto> listBookCommentsDto= bookCommentsService.listAllDetailComments(book_id);
mav.addObject("listBook",book);
mav.addObject("listBookCommentsDto", listBookCommentsDto);
return mav;
}
Finally update js
<c:forEach items="${listBookCommentsDto}" var="bookcommentsDto">
${bookcommentsDto.bookComment}
<h4>${bookcommentsDto.username}</h4>
</c:forEach>
if you do the above changes, the code should work just fine.
I have a few comments for you if you want to improve your code and get better at coding.
When using Hibernate, we must think in terms of entities instead of tables found in the DB. Therefore we do not do inner joins like we do in native sql, instead we use hibernate relationship to define the relationship between the table and we join the entities instead of the tables. You can read on OneToMany, ManyToOne and ManyToMany relationships when it comes to joining entities.
List<Books> book=booksService.bookGetWithId(book_id);
A book_id is returning a list of Books? book_id should be unique per book if ids are used correctly and even if it is returning a list of books, the generic of the list should be Book, not Books; List<Book>, not List<Books>.
When hibernate is implemented correctly, you should be able to retrieve all the data at once and it would be something like:
This is the type of code is which you should be able to achieve after working with hibernate for a while (appx 1 year), however there is still a lot that can be improved about it.
In book's repository
#Query("from book b inner join b.comments c inner join c.users where b.book_id = :book_id")
Book getBook(#Param("book_id") long book_id); // bookService.getBook will call this method
Controller level
Book book = bookService.getBook(bookId); // only this one is querying the DB
List<BookComment> bookComments = book.getComments();
List<User> users = book.getComments().getUsers();
Advance topics to read on (read in same order as posted):
HQL - Hibernate Query Language
N+1 issue with Hibernate
JPA Specifications (for creating dynamic queries)
QueryDsl - an improvement of JPA Specifications
I am learning spring boot caching to apply this concept in our organization's project and I made a sample project called employe cache. I have four methods in my controller and service component insert, update, get, and getAll.For insert and get #Cacheable is working perfectly. Now I am calling getAllEmployee() first time then it is fetching data from the database. After that I am trying to update with #CachePut it updates the value in the database and again I am calling getAllEmployee() then it didn't return updated value from the cache. I also refer to the documentation for #CachePut. I also refer to some other documents like this and this but I didn't solve my problem. Also, When I am calling, no error is raised.
What I Tried is
These are my two APIs from EmplyeeController.java
#PostMapping(value = "/updateSalary")
private Boolean updateSalary(#RequestParam int salary, #RequestParam Integer id) {
return empService.updateSalary(salary, id);
}
#GetMapping(value = "/getAllEmployee")
private Object getAllEmployee() {
List<EmployeeMapping> empList = empService.getAllEmployee();
return !empList.isEmpty() ? empList : "Something went wrong";
}
These are my two methods from EmployeeService.java. I applied different keys to update the method but didn't work. My getAll() method has no parameter so I tried all the keys techniques for no parameter methods from here then also I didn't get any results.
#CachePut(key = "#root.method.name")
public Boolean updateSalary(int salary, int id) {
System.err.println("updateSalary method is calling in service");
if (empRepo.salary(salary, id) != 0) {
return true;
}
return false;
}
#Cacheable(key = "#root.method.name")
public List<EmployeeMapping> getAllEmployee() {
return empRepo.findAllEmployee();
}
These are my two methods from EmployeeRepository.java. I used #SqlResultSetMappings and #NamedNativeQueriesin EmployeeMetaModel.java with EmployeeMapping.java but there is no error in native query in EmployeeMetaModel.java because it's giving result from database.
#Transactional
#Modifying
#Query("update employee_cache e set e.salary = ?1 where e.id = ?2")
int salary(int salary, int id);
#Query(name = "EmployeeListQuery", nativeQuery = true)
List<EmployeeMapping> findAllEmployee();
Kindly help me to get rid of this I just need an updated value from the cache using getAllEmployee() after updateSalary() called.
There is an issue with how you've defined caching via annotations. Your #CachePut and #Cacheable don't use the same cache key. What you should actually have is something like this:
#CachePut(value = "employees", key = "T(org.springframework.cache.interceptor.SimpleKey).EMPTY")
public List<EmployeeMapping> updateSalary(int salary, int id) {
// update salary and return the list of employees
}
#Cacheable(value = "employees")
public List<EmployeeMapping> getAllEmployee() {
// return the list of employees
}
Here #CachePutand #Cacheable have the same cache key.d Now, when you call the updateSalary() method, #CachePut will replace the existing cached value for key "employees", with the result of the method i.e. list of employees with updated salary.
I try to use handle query for updating table in the SQL database.
Code:
#Autowired
private ProducerRepository producerRepository;
public void update(Producer producer){
String name = producer.getProducerName();
long id = producer.getId();
// producerRepository.save(producer); //this method works well.
producerRepository.update(name, id); //handle attempt - throws exeption in this string
}
ProducerRepository:
#Repository
public interface ProducerRepository extends JpaRepository<Producer, Long>{
#Query(nativeQuery = true, value = "UPDATE producer SET producer_name = :pName WHERE id = :id")
Producer update(
#Param("pName") String pName,
#Param("id") long id
);
}
All parameters of the producer entity are correct and producerRepository.save(producer) works well.
(also I out in console name and id fields - all right)
So, I can save producer in the database, but, when I try to use update() method I get the error.
Can not issue data manipulation statements with executeQuery()
PS
sql query in the console also works well
(UPDATE producer SET producer_name = 'some name' WHERE id = ....)
It should be noted that other SQL native queries in repository work correctly. So the spring/hibernate/jdbc settings are correct.
Use annotation #Modifying.
This will trigger the query annotated to the method as updating query
instead of a selecting one.
From 2.2.6 Modifying queries https://docs.spring.io/spring-data/jpa/docs/1.3.4.RELEASE/reference/html/jpa.repositories.html
In case above solution not work use this
#Modifying
#Transactional
#Query(value ="delete from admindata where user_name = :userName AND group_name = :groupName",nativeQuery = true)
public void deleteadminUser(#Param("userName") String userName,#Param("groupName") String groupName);
#Query(nativeQuery = true, value = "UPDATE producer SET producer_name = :pName WHERE id = :id")
Producer update(
#Param("pName") String pName,
#Param("id") long id
);
your update method is returning Producer Object, the return should be either int or void, since you are returning Producer entity, Spring JPA is thinking that it has to fetch the object instead of updating it, that is the reason it is executing executeQuery, instead of executeUpdate and also you need to #Modifying annotation.
I have an entity that is exposed by the following repository:
public interface InsertRepository extends PagingAndSortingRepository<InsertEntity, Long>, QueryDslPredicateExecutor<InsertEntity>, QueryDslBinderCustomizer<QInsertEntity> {
#Override
default void customize(QuerydslBindings bindings, QInsertEntity insert) {
bindings.bind(String.class).all(StringPath path, Collection<? extends String> values) -> {
BooleanBuilder predicate = new BooleanBuilder();
values.forEach(value -> predicate.or(path.containsIgnoreCase(value)));
return predicate;
});
}
}
What I'd like it to do is that all GET query parameters are chained as logical OR so that a query like ?description=searchText&customerName=searchText would execute an SQL query that looks as follows:
WHERE description LIKE '%searchText%' OR customerName LIKE '%searchText%'
However, I must be doing something wrong because it doesn't work - it is putting all query parameters into an AND query. That results in only those records being selected that contain searchText in customerName AND their description.
You can use named parameters such as
Example 53. Using named parameters
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(#Param("lastname") String lastname,
#Param("firstname") String firstname);
}
The answer provided by #kafkas is not accurate at all. There are quite few problems with it:
Annotation #Query is obsolette - Spring automatically does it for you, you just need to type the name of the method properly.
Annotations #Param are obsolette - Spring automatically takes parameters in given order, matched with those in method name.
You return single User entity, but you still use findBy instead of findOne - this leads to an error, if more than one record is found.
The last, but not least - provided method will not use LIKE comparation, but equals instead. You should use findByXXXContaining if You wish to launch SQL query like: ... WHERE firstname LIKE "%name%"
Using Spring, I suggest not using JpaRepository if You don't need to. The simplest implementation is CrudRepository and it covers most use cases.
In summary, Your method should be simplified and look somewhere like that:
public interface UserRepository extends CrudRepository<User, Long> {
User findOneByLastnameContainingOrFirstnameContaining(String lastname, String firstname);
}
This should result in query:
SELECT * FROM User u WHERE u.lastname LIKE '%lastname' OR u.firstname LIKE '%firstname%;'
I'm working on a project with Spring Data JPA. and I want to add some customized behavior to the repository.
At the moment I'm trying to create a view by executing the method
The repository class is as follows.
public interface MyQueryRepository extends JpaRepository<MyQuery, Long>, MyQueryRepositoryCustom {
public MyQuery findById(long id);
public list executeMyQuery();
}
This is the customization:
public class MyQueryRepositoryImpl implements MyQueryRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
public List executeMyQuery() {
return entityManager.createQuery("CREATE VIEW result_set AS select record FROM my_data").getResultList();
}
}
Howeve, I get the following error.
[ERROR] [http-bio-8080-exec-10] ErrorCounter - line 1:1: unexpected token: CREATE
antlr.NoViableAltException: unexpected token: CREATE
at org.hibernate.hql.internal.antlr.HqlBaseParser.statement(HqlBaseParser.java:198) [hibernate-core-5.1.1.Final.jar:5.1.1.Final]
The above code works for SELECT queries.
Is there any other way, that I could CREATE VIEW using entity managers. Thanks in advance
To work with getResultList(), you have to make Select and not CREATE? UPDATE? Or DELETE.
So if you want to CREATE a view then you have to execute your query, and not getResultList(), to create a view i suggest to use CreateNativeQuery for example :
Query q = entityManager.createNativeQuery("CREATE VIEW result_set AS
select record FROM my_data");
q.executeUpdate();
Else if you want to get values from your query then you have to change your query to SELECT something FROM result_set