Problem with saving an object with rest template, Spring web app - spring

So I'm building a flashcard app with Spring, JPA, MySQL and using Thymeleaf.
At first when i started building I only used MVC. All functionality for saving a new user, a new flashcard and a new topic worked.
Now I connected it with a project with a restcontroller and did some modifactions.
The problem is that I can't seem to save a new flashcard (although i can save a topic and a user)
Can someone spot where i've made a mistake?
(note. i have a JPARepository in the backend)
#Controller-class
//This does not work
#PostMapping("/save")
public String set(#ModelAttribute Flashcard flashcard){
restTemplate.postForObject("http://localhost:8081/api/card/save", flashcard, Flashcard.class);
return "redirect:/";
}
//this works
#PostMapping("/savePlayer")
public String savePlayer(#ModelAttribute Player player){
restTemplate.postForObject("http://localhost:8081/api/player/save", player, Player.class);
return "homepage";
}
**#RestController-class**
#PostMapping("/api/card/save")
public Flashcard createFlashcard(#RequestBody Flashcard flashcard) {
log.info("New Card added to FlashcardRepository - {}", flashcard);
return repository.save(flashcard);
}
**Flashcard-class**
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "card_id")
Long id;
String term;
String answer;
#ManyToOne
#JsonBackReference
#JoinColumn(name = "topic_id")
Topic topic;
Error message:
2022-07-15 10:17:41.555 WARN 2016 --- [nio-8081-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1048, SQLState: 23000
2022-07-15 10:17:41.556 ERROR 2016 --- [nio-8081-exec-4] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'topic_id' cannot be null
2022-07-15 10:17:41.608 ERROR 2016 --- [nio-8081-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

Related

Why does rollback occur in spring jpa after first find

I'm not sure why rollback occurs after the first find query. The first query perfectly works fine, but rollback constantly occurs if I try to do a query again.
This is the Controller.
//....
#GetMapping("/s-id/{s-id}")
public ResponseEntity<DefaultResponseDto<Object>> findManyBySId(HttpServletRequest servletRequest,
#PathVariable(name = "s-id")
String sId) {
// ...
List<Member> members = memberService.findManyBySpotrightId(spotrightId);
// ....
}
//...
This is the Service.
#Transactional(readOnly = true)
public List<Member> findManyBySId(String spotrightId) {
try {
return memberRepository.findBySpotrightIdContainsAndIsDeletedIsFalse(spotrightId);
} catch (RuntimeException e) {
throw new CustomException(SERVER_ERROR);
}
}
This is the Repository.
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findBySIdContainsAndIsDeletedIsFalse(String sId);
}
This is the error message that I get in the console
2023-02-06 19:21:48.470 INFO 62504 --- [nio-8080-exec-9] c.s.s.common.aop.TraceAspect : [START] MemberService | findManyBySpotrightId null | sId = admin
2023-02-06 19:21:48.473 INFO 62504 --- [nio-8080-exec-9] c.s.s.common.aop.TraceAspect : [START] MemberRepository | findBySIdContainsAndIsDeletedIsFalse null
2023-02-06 19:21:48.485 INFO 62504 --- [nio-8080-exec-9] p6spy : #1675678908485 | took 1ms | rollback | connection 8| url jdbc:mysql://localhost:3306/spot?serverTimezone=UTC&characterEncoding=UTF-8
;
2023-02-06 19:21:48.498 ERROR 62504 --- [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [\] did not match expected type [java.lang.String (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [\] did not match expected type [java.lang.String (n/a)]] with root cause
java.lang.IllegalArgumentException: Parameter value [\] did not match expected type [java.lang.String (n/a)]
Why does the rollback occur? And How do I fix this problem?
I found out that it is a bug from Hibernate. I fixed it by including #Param("sId") to parameter like below.
List<Member> findBySIdContainingAndIsDeletedIsFalse(#Param("sId") String sId);

String index out of range: 0 when stored procedure returning empty value in spring boot

I have a created a spring boot app which is calling a Stored procedure to fetch data using CrudRepository. Below is the implementation of Repo interface:
public interface MyRepository extends CrudRepository<EmpEntity, Long> {
#Query(value = "call myStoredProc(:empID, :empProv);", nativeQuery = true)
Map<String, String> findWithNumber(#Param("empID ") String empID, #Param("empProv ") String empProv);
}
The stored procedure is returning 10 variables and out of those 1 value is returning blank value. Due to this behavior I am getting following error:
2020-06-10 08:02:46.118 ERROR 23724 --- [nio-8050-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.StringIndexOutOfBoundsException: String index out of range: 0] with root cause
java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(String.java:658) ~[na:1.8.0_144]
at org.hibernate.type.descriptor.java.CharacterTypeDescriptor.wrap(CharacterTypeDescriptor.java:61) ~[hibernate-core-5.4.9.Final.jar!/:5.4.9.Final]
at org.hibernate.type.descriptor.java.CharacterTypeDescriptor.wrap(CharacterTypeDescriptor.java:16) ~[hibernate-core-5.4.9.Final.jar!/:5.4.9.Final]
at org.hibernate.type.descriptor.sql.VarcharTypeDescriptor$2.doExtract(VarcharTypeDescriptor.java:62) ~[hibernate-core-5.4.9.Final.jar!/:5.4.9.Final]
at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:47) ~[hibernate-core-5.4.9.Final.jar!/:5.4.9.Final]
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:257) ~[hibernate-core-5.4.9.Final.jar!/:5.4.9.Final]
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253) ~[hibernate-core-5.4.9.Final.jar!/:5.4.9.Final]
at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:249) ~[hibernate-core-5.4.9.Final.jar!/:5.4.9.Final]
I can fix the issue by putting NULLIF(a.addressType,"") addressType in stored procedure but I want to understand the reason behind the issue and also the possible fix in Spring boot app.

Spring boot design delete RESTful API through JPA got error

I need design delete API, but I got error. I still not get idea where was wrong.
Repository:
public interface ProductRepository extends JpaRepository<Product, Integer> {
#Modifying
#Query(nativeQuery=true, value = "delete from product where productid = ?1")
void deleteProductByProductID(int productID);
}
Controller:
#DeleteMapping(path = "/delete/{id}")
public #ResponseBody
void deleteProduct(#PathVariable("id")int productID) {
productRepository.deleteProductByProductID(productID);
}
API test error:
{
"timestamp": "2018-04-08T08:46:15.639+0000",
"status": 500,
"error": "Internal Server Error",
"message": "could not extract ResultSet; nested exception is org.hibernate.exception.GenericJDBCException: could not extract ResultSet",
"path": "/v1/product/delete/30"
}
IDE error:
2018-04-08 01:36:26.890 WARN 13629 --- [nio-8080-exec-1]
o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState:
S1009 2018-04-08 01:36:26.890 ERROR 13629 --- [nio-8080-exec-1]
o.h.engine.jdbc.spi.SqlExceptionHelper : Can not issue data
manipulation statements with executeQuery(). 2018-04-08 01:36:26.906
ERROR 13629 --- [nio-8080-exec-1]
o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for
servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is
org.springframework.orm.jpa.JpaSystemException: could not extract
ResultSet; nested exception is
org.hibernate.exception.GenericJDBCException: could not extract
ResultSet] with root cause

Running a native insert query in spring boot

I am trying to run a simple insert query in spring boot but I am unable to do so.
Query:
#Modifying
#Query(value = "insert into Local(userId,name,address,pin) VALUES (:userId,:name,:address,:pin)", nativeQuery = true)
List < Local > insertattributes(#Param("userId")String userId,#Param("address") String address ,#Param("name")String name,#Param("pin") String pin);
Controller:
#RequestMapping("insertattributes/{userId}/{name}/{address}/{pin}")
#ResponseBody
public List < Local > insertAttributes(#PathVariable String userId, #PathVariable String name, #PathVariable String address, #PathVariable String pin) {
return localService.insertattributes(userId,name,address,pin);
}
Error:
o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: S1009
o.h.engine.jdbc.spi.SqlExceptionHelper : Can not issue data manipulation statements with executeQuery().
[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: could not extract ResultSet; nested exception is org.hibernate.exception.GenericJDBCException: could not extract ResultSet] with root cause
java.sql.SQLException: Can not issue data manipulation statements with executeQuery().
An insert statement doesn't return the inserted record. Since you're expecting a list to be returned executeQuery() is executed by Spring. What you want is the executiong of executeUpdate() which is only executed for void methods.
See also Cannot issue data manipulation statements with executeQuery()

Testing Rest Controllers in Spring Boot using Standalone MockMvc

I am trying to put some unit/integration tests on my code,
I've a typical rest application build with spring boot, I am trying to test the AdminController save method:
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Void> save(#RequestBody #Valid User user) {
user.setRole(Role.ROLE_ADMIN);
user.setPassword(passwordEncoder.encode(user.getPassword()));
return super.save(user);
}
The method is straight forward and it check the duplicate through the database, as I making the User object's username property unique:
#NotNull
#Column(updatable = false, unique = true)
private String username;
When I try to save two users with the same username, the server refuses with this out-of-the-box JSON output: (Http code: 500)
{
"timestamp": 1473942296273,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.dao.DataIntegrityViolationException",
"message": "could not execute statement; SQL [n/a]; constraint [uk_sb8bbouer5wak8vyiiy4pf2bx]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
"path": "/api/admins"
}
I am ok with this, as it appears spring boot default behaviour is to send errors to /error (which is handled by BasicErrorController) that handle the exception and return this pretty json output:
[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [uk_sb8bbouer5wak8vyiiy4pf2bx]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing POST request for [/error]
RequestMappingHandlerMapping : Looking up handler method for path /error
RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
DefaultListableBeanFactory : Returning cached instance of singleton bean 'basicErrorController'
OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
HttpEntityMethodProcessor : Written [{timestamp=Thu Sep 15 16:09:07 AST 2016, status=500, error=Internal Server Error, exception=org.springframework.dao.DataIntegrityViolationException, message=could not execute statement; SQL [n/a]; constraint [uk_sb8bbouer5wak8vyiiy4pf2bx]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement, path=/api/admins}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#1482cc8]
But when I try to test this using mockito (see full test class):
#Transactional
#Test
public void testSaveDupValidUser() throws Exception {
User user = new User();
user.setUsername("admin");
this.mockMvc.perform(post("/api/admins")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(user)))
.andDo(print())
.andExpect(status().isOk());
this.mockMvc.perform(post("/api/admins")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(user)))
.andDo(print())
.andExpect(status().isInternalServerError());
}
instead of getting 500 internal server error, I got the following exception from junit:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["UK_SB8BBOUER5WAK8VYIIY4PF2BX_INDEX_3 ON PUBLIC.""user""(USERNAME) VALUES ('admin', 1)"; SQL statement:
insert into "user" (id, created_by, created_date, modified_by, modified_date, enabled, password, role, username) values (null, ?, ?, ?, ?, ?, ?, ?, ?) [23505-192]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
So, My question is:
How to enable make the dispatcher servlet to forward to /error and return the same error as if the application is running (not under test)?
while keeping use MockMvc standealone, and without writing code in the rest method to handle the exception and return code 500.
(I think this might be impossible, so Should I assert on the exception instead? Or should I use webAppContextSetup? What is the best practice here? - jHipster for example uses standalone MockMvc in all its tests)
Related: unit test Spring MissingServletRequestParameterException JSON response (but mine not fixed)
During runtime spring registers a error page at the servlet container and forwards all not handled errors there.
The MockMvcServer does not fully support forwarding, and I think that is the reason why you see a different result in your tests.
When saving a user fails, because another one with the same name already exists, this is not an (unexpected) internal server error. Instead you should catch the exception in the controller and return a HTTP 400 (Bad Request). This tells the client, the server is ok, but something with the request is not.
You can add a ExceptionHandler to create the response for different types of exceptions.
All HTTP 400-499 are client errors, so they will not be forwarded to the error page, which is intended.
If the exception is handled, you should also receive the correct Http status and response in the MockMvc test.

Resources