I followed this spring tutorial and everything worked fine. After this, I decided to add delete and modify funcionalities. I've implemented them, but when I try to use them, I got the following error:
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/delete"}
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/modify"}
The commands that I'm executing:
curl localhost:8080/demo/delete -d name=First
curl localhost:8080/demo/modify -d name=First -d email=abc#gmail.com
//if the name doesn't exist in both methods, it will return "Nonexistent user!"
Below are the following code of:
MainController.java
package com.example.accessingdatamysql;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping(path="/demo")
public class MainController {
#Autowired
private UserRepository userRepository;
#PostMapping(path="/add")
public #ResponseBody String addNewUser (#RequestParam String name,
#RequestParam String email) {
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "Saved\n";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<User> getAllUsers() {
return userRepository.findAll();
}
/* ----------------- NEW METHODS THAT I'VE CREATED ----------------- */
#DeleteMapping(path="/delete")
public String delete(#RequestParam String name) throws Exception {
if(userRepository.findByName(name).equals(null)) {
System.out.println("Nonexistent user!\n");
}
userRepository.deleteByName(name);
return "User successfully deleted!\n";
}
#PutMapping(path="/modify")
public String modify(#RequestParam String name, #RequestParam String email) throws Exception {
if(userRepository.findByName(name).equals(null)) {
System.out.println("Nonexistent user!\n");
}
userRepository.deleteByName(name);
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "User successfully modified!\n";
}
}
User.java
package com.example.accessingdatamysql;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserRepository.java
package com.example.accessingdatamysql;
import org.springframework.data.repository.CrudRepository;
import com.example.accessingdatamysql.User;
public interface UserRepository extends CrudRepository<User, Integer> {
public User findByName(String name);
public void deleteByName(String name);
}
I've no idea what's happening here. I've searched for other similar questions like that with the same error but none of them solved my problem.
You are executing the calls with the method POST rather than DELETE for /demo/delete and PUT for /demo/modify.
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/delete"}
{"status":405,"error":"Method Not Allowed","message":"Request method 'POST' not supported","path":"/demo/modify"}
You are not showing how are you executing those failing calls, but for example, if you are using a GUI client such Postman to simulate the calls, check that you are selecting the proper HTTP method.
And if you are using the curl library in the terminal pay attention to the method set:
curl -X "DELETE" http://your.url/demo/delete ...
curl -X "PUT" http://your.url/demo/modify ...
After following #Dez's answer, the problem was solved but I was getting other error:
No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
I solved it by adding #Transactional on DELETE and PUT methods in MainController class:
#DeleteMapping(path="/delete")
#Transactional //added
public void delete(#RequestParam String name) throws Exception { (...) }
#PutMapping(path="/modify")
#Transactional //added
public void modify(#RequestParam String name, #RequestParam String email) throws Exception { (...) }
And modified the exception throwing condition, that was always returning false:
if(userRepository.findByName(name).equals(null)) {
throw new Exception("Nonexistent user!");
}
to
if(userRepository.findByName(name) == null) {
throw new Exception("Nonexistent user!");
}
This is how #DeleteMapping and #PutMapping work. These annotations are shortcuts to #RequestMapping with method attribute fixed to DELETE or PUT correspondingly. To allow multiple HTTP methods for an endpoint you should list them explicitly:
#RequestMapping(path = "/modify", method = { RequestMethod.DELETE, RequestMethod.POST })
public String modify(...){
...
}
Related
import io.micronaut.core.annotation.NonNull;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.r2dbc.annotation.R2dbcRepository;
import io.micronaut.data.repository.reactive.ReactiveStreamsCrudRepository;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.runtime.Micronaut;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
#Controller
#RequiredArgsConstructor
class CustomerController {
private final CustomerRepository customerRepository;
#Get
public Flux<Customer> getAll() {
return customerRepository.findAll();
}
#Get("/test")
public Mono<Customer> test() {
return Mono.from(customerRepository.findById(1L)).map(e -> {
System.out.println(e);
return e;
});
// System.out.println(customerRepository);
// return Mono.just("TEST");
}
#Post
public Mono<Customer> create(#Body Customer customer) {
Customer saveCustomer = new Customer(customer.getName(), customer.getSurname());
return Mono.from(customerRepository.save(saveCustomer));
}
}
//#R2dbcRepository(dialect = Dialect.SQL_SERVER)
interface CustomerRepository extends ReactiveStreamsCrudRepository<Customer, Long> {
// #NonNull
// #Override
// Mono<Customer> findById(#NonNull #NotNull Long id);
//
// #NonNull
// #Override
// Mono<Customer> save(#NonNull #NotNull Customer customer);
//
#NonNull
#Override
Flux<Customer> findAll();
}
#MappedEntity("customer")
class Customer {
#Id
#GeneratedValue
private Long id;
private final String name;
private final String surname;
public Customer(String name, String surname) {
this.name = name;
this.surname = surname;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
}
**
Trying to call a repositry through endpoint but keep getting error
**
Context does not contain key: io.micronaut.tx.STATUS
at reactor.util.context.Context1.get(Context1.java:67)
at io.micronaut.data.r2dbc.operations.DefaultR2dbcRepositoryOperations.lambda$withTransaction$27(DefaultR2dbcRepositoryOperations.java:441)
at reactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:49)
at reactor.core.publisher.InternalFluxOperator.subscribe(InternalFluxOperator.java:62)
at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:54)
at reactor.core.publisher.InternalFluxOperator.subscribe(InternalFluxOperator.java:62)
I've faced the same issue.
Check this:
#Get
public Flowable<Customer> getAll() {
return Flowable.fromPublisher(customerRepository.findAll());
}
and where is Mono use Single.fromPublisher(yourMono)
I encountered the same issue with micronaut 3.
After some tests i found that, in my specific case, this issue was caused by using the following modules:
micronaut-security-jwt
micronaut-reactor
micronaut-jackson-xml
If you remove all of these dependencies, the issue disappear.
I think that this problem is caused by the above micronaut modules, so i opened an issue on the micronaut-r2dbc github page with the hope they solve this issue ASAP.
EDIT:
they fixed this bug releasing the new micronaut version 3.0.1!!!
I have 3 different method in controller for get requests.
-the 1st one to get a user by id with a path variable:
#GetMapping(path="/{id}")
public ResponseEntity<UserInfoDTO> getUserById(#PathVariable Long id)
The 2nd gets a user based on the username parameter:
public ResponseEntity<UserInfoDTO> getUserByUsername(#RequestParam String username)
And finally another one to get all users
public ResponseEntity<List<UserInfoDTO>> getAllUsers()
What should be the #GetMapping for the 2nd and 3rd method?
For exemple #GetMapping for all users and #GetMapping(path="/") for a user by username?
Or whatever...
Thanks.
Defining the Mappings purely depends on the context of your application and its usecases.
We can define a context prefixed by users and modified mappings are show in the snippet below and at the time of invocation it can be called like mentioned in the comments,
#GetMapping(path="/users/")
public ResponseEntity<UserInfoDTO> getUserByUsername(#RequestParam String username) {
}
// GET: <protocol>://<hostUrl>/users?username=<username>
#GetMapping(path="/users")
public ResponseEntity<List<UserInfoDTO>> getAllUsers() {
}
// GET: <protocol>://<hostUrl>/users
#GetMapping(path="/users/{id}")
public ResponseEntity<UserInfoDTO> getUserById(#PathVariable Long id)
// GET: <protocol>://<hostUrl>/users/<userid>
For example, optional username param:
#GetMapping(path = "/")
public ResponseEntity<?> getUserByUsername(#RequestParam(required = false) final String username) {
if (username != null) {
// http://localhost:8080/?username=myname
return new ResponseEntity<>(new UserInfoDTO("by username: " + username), HttpStatus.OK);
} else {
// http://localhost:8080/
return getAllUsers();
}
}
private ResponseEntity<List<UserInfoDTO>> getAllUsers() {
return new ResponseEntity<>(List.of(new UserInfoDTO("user1-of-all"), new UserInfoDTO("user2-of-all")),
HttpStatus.OK);
}
public static class UserInfoDTO {
public UserInfoDTO(final String name) {
this.name = name;
}
private final String name;
public String getName() {
return name;
}
}
Problem: a new table is created once when I make a post request through The bash console. The rest of the queries go to the new table.
Than he does not like those databases which are available. As I understand - they just don't know, but I don't know how to direct it in the right. Although all variables are also named.
A problem was found created due to an Entity annotation in the Message class. Please tell me how to make it added to an existing table, tried #Table(name = "ApiTable") to an existing one, and it generates a new api_table.. Also don't quite understand what needs to be added/changed to accept json post requests.
Application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#EnableJpaRepositories("com.example.api")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MainController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping(path="/demo") /
public class MainController {
#Autowired
private UserRepository TestApi;
#PostMapping(path="/add")
public #ResponseBody String addNewUser (#RequestParam String name
, #RequestParam String email) {
Message n = new Message();
n.setName(name);
n.setEmail(email);
TestApi.save(n);
return "Saved";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<Message> getAllUsers() {
return TestApi.findAll();
}
}
Message
import javax.persistence.*;
#Entity
public class Message {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
UserRepository
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<Message, Integer> {
}
application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost/Test?useUnicode=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
The problem seems to be Spring Boot's default naming strategy which you'd have to replace.
Spring Boot's default naming strategy now seems to include converting camelCase to snake_case so you need to choose a different one (or implement your own).
Here's some more info on the topic: Hibernate naming strategy changing table names
I am using Spring Boot 4, Hibernate and JPA annotations. I ran into this error
org.hibernate.PersistentObjectException: detached entity passed to
persist.
I tried searching through the internet and couldn't quite get the proper answer. I tried to use merge instead of persist and it did not work. Besides I think merge is used when updating a resource.
Here is my code:
`
package com.matome.users.login.stats.springbootStarter.model;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.NamedQueries;
import com.matome.users.login.stats.springbootStarter.userconstants.UserRepositoryConstants;
#NamedQueries({
#NamedQuery(name = UserRepositoryConstants.NAME_GET_ALL_USERS,
query = UserRepositoryConstants.QUERY_GET_ALL_USERS),
#NamedQuery(name = UserRepositoryConstants.NAME_GET_USER_BY_ID,
query = UserRepositoryConstants.QUERY_GET_USER_BY_ID),
#NamedQuery(name = UserRepositoryConstants.NAME_GET_USER_BY_USERNAME,
query = UserRepositoryConstants.QUERY_GET_USER_BY_USERNAME),
#NamedQuery(name = UserRepositoryConstants.NAME_DELETE_USER,
query = UserRepositoryConstants.QUERY_DELETE_USER)
})
#Entity
public class User implements Serializable{
private static final long serialVersionUID = 1L;
#Id #GeneratedValue(strategy = GenerationType.AUTO)
Long id;
String username;
String password;
String phone;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
`
CRUD service
`
package com.matome.users.login.stats.springbootStarter.CRUDService;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.repository.UserRepository;
#Service
#Transactional
public class UserCRUDService {
#Autowired
UserRepository userRepository;
public User createUser(User user) {
userRepository.save(user);
return user;
}
public User updateUser(User user) {
User existingUser = userRepository.getUserById(user.getId());
if (existingUser == null) {
throw new NoResultException();
}
existingUser.setId(user.getId());
existingUser.setUsername(user.getUsername());
existingUser.setPhone(user.getPhone());
existingUser.setPassword(user.getPassword());
userRepository.update(user);
return user;
}
public User deleteUser(long id) {
User user = userRepository.getUserById(id);
if (user == null) {
throw new NoResultException();
}
userRepository.delete(user);
return user;
}
}
`
Repository
`
package com.matome.users.login.stats.springbootStarter.repository;
import java.util.List;
import javax.persistence.TypedQuery;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.userconstants.UserRepositoryConstants;
#Repository
#Transactional
public class UserRepository extends AbstractRepository<User> {
TypedQuery<User> query;
public List<User> getAllUsers() {
query = entityManager.createNamedQuery(UserRepositoryConstants.NAME_GET_ALL_USERS, User.class);
return query.getResultList();
}
public User getUserById(long id) {
query = entityManager.createNamedQuery(UserRepositoryConstants.NAME_GET_USER_BY_ID, User.class);
query.setParameter("id", id);
return query.getSingleResult();
}
public User getUserByUsername(String username) {
query = entityManager.createNamedQuery(UserRepositoryConstants.NAME_GET_USER_BY_USERNAME, User.class);
query.setParameter("username", username);
return query.getSingleResult();
}
}
`
Abstract repository
`
package com.matome.users.login.stats.springbootStarter.repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public abstract class AbstractRepository<T> {
#PersistenceContext
protected EntityManager entityManager;
#Transactional(propagation = Propagation.MANDATORY)
public void save(T entity) {
entityManager.persist(entity);
entityManager.flush();
entityManager.refresh(entity);
}
#Transactional
public T update(T entity) {
return entityManager.merge(entity);
}
#Transactional
public void delete(T entity) {
entityManager.remove(entity);
}
}
`
Factory
`
package com.matome.users.login.stats.springbootStarter.factories;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.matome.users.login.stats.springbootStarter.BackingForm.UserBackingForm;
import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.viewmodels.UserViewModel;
#Service
public class UserFactory {
public UserViewModel createViewModel(User user) {
UserViewModel viewModel = new UserViewModel();
viewModel.setId(user.getId());
viewModel.setUsername(user.getUsername());
viewModel.setPhone(user.getPhone());
return viewModel;
}
public List<UserViewModel> createVewModels(List<User> users) {
List<UserViewModel> viewModels = new ArrayList<>();
if (users != null) {
for (User user : users) {
viewModels.add(createViewModel(user));
}
}
return viewModels;
}
public User createEntity(UserBackingForm userBackingForm) {
User user = new User();
user.setId(userBackingForm.getId());
user.setUsername(userBackingForm.getUsername());
user.setPassword(userBackingForm.getPassword());
user.setPhone(userBackingForm.getPhone());
return user;
}
}
`
Backing form
`
package com.matome.users.login.stats.springbootStarter.BackingForm;
public class UserBackingForm {
private long id;
private String username;
private String password;
private String phone;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
`
Controller
`
package com.matome.users.login.stats.springbootStarter.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.matome.users.login.stats.springbootStarter.BackingForm.UserBackingForm;
import com.matome.users.login.stats.springbootStarter.CRUDService.UserCRUDService;
import com.matome.users.login.stats.springbootStarter.factories.UserFactory;
import com.matome.users.login.stats.springbootStarter.model.User;
import com.matome.users.login.stats.springbootStarter.repository.UserRepository;
import com.matome.users.login.stats.springbootStarter.viewmodels.UserViewModel;
#RestController
#RequestMapping("api")
public class UserController {
#Autowired
private UserRepository userRepository;
#Autowired
private UserCRUDService userCRUDService;
#Autowired
private UserFactory userFactory;
#RequestMapping(value = "/users", method = RequestMethod.GET)
public Map<String, Object> getAllUsers() {
Map<String, Object> map = new HashMap<>();
map.put("users", userRepository.getAllUsers());
return map;
}
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public Map<String, Object> getUserById(#PathVariable long id) {
Map<String, Object> map = new HashMap<>();
map.put("user", userRepository.getUserById(id));
return map;
}
#RequestMapping(value = "/user/add", method = RequestMethod.POST)
public UserViewModel addUser(#RequestBody UserBackingForm form) {
User user = userFactory.createEntity(form);
//user.setIsActive(true);
user = userCRUDService.createUser(user);
return userFactory.createViewModel(user);
}
#RequestMapping(value = "/user/add/{id}", method = RequestMethod.PUT)
public Map<String, Object> updateUser(#PathVariable("id") Long id, #RequestBody User user) {
Map<String, Object> map = new HashMap<>();
user.setId(id);
map.put("updatedUser", userCRUDService.updateUser(user));
return map;
}
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String testFinal() {
return "User test sucessfully";
}
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteUser(#PathVariable("id") Long id) {
User user = userRepository.getUserById(id);
userRepository.delete(user);
}
}
`
View Model
`
package com.matome.users.login.stats.springbootStarter.viewmodels;
public class UserViewModel extends BaseViewModel<Long> {
private String username;
private String phone;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
`
base ViewModel
`
package com.matome.users.login.stats.springbootStarter.viewmodels;
public abstract class BaseViewModel<T> {
private T id;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
#SuppressWarnings("unchecked")
BaseViewModel<T> other = (BaseViewModel<T>) obj;
if (getId() == null) {
if (other.getId() != null)
return false;
} else if (!getId().equals(other.getId()))
return false;
return true;
}
}
`
Main
`
package com.matome.users.login.stats.springbootStarter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Main { public static void main(String[] args) {
SpringApplication.run(Main.class,args);
}
}
`
Stack Trace
`
org.hibernate.PersistentObjectException: detached entity passed to persist: com.matome.users.login.stats.springbootStarter.model.User
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347) ~[spring-orm-4.3.4.RELEASE.jar:4.3.4.RELEASE]
at com.sun.proxy.$Proxy78.persist(Unknown Source) ~[na:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
`
In my opinion the problem lies in the fact that you have declared the id of User entity as auto-generated:
#Id #GeneratedValue(strategy = GenerationType.AUTO)
Long id;
and when you create the User object you set the id manually:
public User createEntity(UserBackingForm userBackingForm) {
User user = new User();
user.setId(userBackingForm.getId());
...
and thus when you pass that to persist / save, the Persistence Provider will not allow that as it expects that the id will not be set by the client.
The bottom line is that you should not set the id regarding the current configuration when you are aiming at persisting a new entry and you should be fine.
Can you provide a full stack trace ?
Some hints for you :
You should only set #Transactional at Service level, not dao / repository.
Entitymanager refresh method means get data from DB to recreate an entity. If you just persist the entity, there is no need to refresh it.
#Transactional(propagation = Propagation.MANDATORY)
public void save(T entity) {
entityManager.persist(entity);
entityManager.flush(); // This is not really usefull on a small transaction
entityManager.refresh(entity); // No need for that : persist means entity already in sync
}
On update side, if you have a managed entity there is no need to sync it back to the DB, JPA take care of that automatically. (and merge push entity in the db forcing an update of all column) :
public User updateUser(User user) {
User existingUser = userRepository.getUserById(user.getId());
// ...
existingUser.setPassword(user.getPassword());
// ...
userRepository.update(user); // This is not needed !
return user;
}
If you want simplest things, you should take a look at lombok. It will help you write simpliest bean ( #Data to generate getter/setter ...)
You use Spring, so take also a look at spring-Data-JPA, It writes for you some cumberstone code.
I am learning Spring and I'm working my way through the tutorials. I have a form submission example and am trying to store the result using Hibernate/JPA. The example works with the following Entity:
package hello;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
//import javax.persistence.Entity;
//import javax.persistence.GeneratedValue;
//import javax.persistence.GenerationType;
//import javax.persistence.Id;
//#Entity
public class Comment {
// #Id
// #GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#NotNull
#Size(min=2, max=150)
private String comment;
#Size(min=2, max=150)
private String name;
#Override
public String toString() {
return String.format("Comment[id=%d, comment='%s', name='%s']", id, comment, name);
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
However if I remove the JPA commented lines the example throws the following exception when validating the form:
2016-11-04 14:59:32.716 WARN 17749 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to bind request element: org.springframework.beans.TypeMismatchException: Failed to convert value of type [java.lang.String] to required type [hello.Comment]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'Comment'; nested exception is java.lang.NumberFormatException: For input string: "Comment field..."
This seems to suggest it is trying to convert the value submitted for the "comment" field of the entity into a Long. Is it a case of providing an annotation to specify that the field is a String? I would have thought it would check the java type for this. Or am I doing something else wrong?
If you need the rest of the code let me know.
Edit: The error stopped when I removed this repository:
package hello;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface CommentRepository extends CrudRepository<Comment, Long> {
List<Comment> findByName(String name);
}
Is there something wrong with the "Comment,Long" part? I copied these from the example and looking at the API for CrudRepository it looks correct as the Entity type is Comment and the ID type is Long.
I'm binding the values in a WebMvcConfigurerAdapter in the following method:
#PostMapping("/")
public String checkPersonInfo(#Valid Comment comment, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}