I have a question to ask:
I have to tables user and user_login joined OneToOne by user.id -> user_login.user_id.
The issue is when I do .updateObject(user) I get 2 queries executed:
Hibernate: insert into User (created, modified, email, first_name,
last_name) values (?, ?, ?, ?, ?) Hibernate: insert into user_login
(created, modified, password, user_id) values (?, ?, ?, ?) [2012-08-15
12:15:04,192] [ERROR] [http-bio-8080-exec-1] SqlExceptionHelper [144]:
Column 'user_id' cannot be null
and looks like there is no reference between 2 objects. If into the Entity User, method setUserLogin I add line
userLogin.setUser(this); its working but I dont find this way elegant honestly. Is there anything I missed in entity configuration
maybe that does not do that automatically ?
Thank you
Here are my Entities
#Entity
#NamedQueries({ #NamedQuery(name = "user.list", query = "select u from User u") })
public class User implements java.io.Serializable {
#Column(name = "first_name", nullable = true)
private String firstName;
#OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name="user_id", nullable=false)
private UserLogin userLogin;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public UserLogin getUserLogin() {
return userLogin;
}
public void setUserLogin(UserLogin userLogin) {
this.userLogin = userLogin;
//userLogin.setUser(this); THIS IS THE LINE THAT FIXES IT, BUT I DONT FIND THIS WAY ELEGANT
}
}
#Entity
#Table(name="user_login")
public class UserLogin implements java.io.Serializable {
#Column(name = "password", nullable = false)
private String password;
#OneToOne(optional = false, fetch = FetchType.LAZY)
private User user;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
JSP File:
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<html>
<head>
<title>Registration Page</title>
</head>
<body>
<form:form action="/test" commandName="user">
<tr>
<td>User Name :</td>
<td><form:input path="firstName" /></td>
</tr>
<tr>
<td>Password :</td>
<td><form:input path="userLogin.password" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Register"></td>
</tr>
</table>
</form:form>
</body>
</html>
Spring Controller:
#Controller(value = "/")
public class Test {
#Autowired
UserServiceImpl userServiceImpl;
#RequestMapping(method = RequestMethod.GET, value = "/test")
public void test(ModelMap model) {
model.addAttribute("user", new User());
}
#RequestMapping(method = RequestMethod.POST, value = "/test")
public void test(User user) {
userServiceImpl.update(user);
}
}
As usual, bidirectional relationships do have owning side. Owning side of relationship is attribute that is referenced by mappedBy. In your case attribute user in UserLogin entity is the owning side.
When relationship is persisted to the database, only owning side is consulted. This means, that you have to set value for user attribute to be able to persist. To keep also entity graph in memory consistent both sides of the relationship should be set.
In JPA 2.0 specification this is told with following words:
Bidirectional relationships between managed entities will be persisted
based on references held by the owning side of the relationship. It is
the developer’s responsibility to keep the in-memory references held
on the owning side and those held on the inverse side consistent with
each other when they change.
Related
I have a Spring Boot application that needs adds a post to a feed list. A post is written by a user and consists of a content and several attachments. The database has 3 tables: post, attachment and user.
The main class of the application is:
#SpringBootApplication
public class SocialMediaApplication {
public static void main(String[] args) {
SpringApplication.run(SocialMediaApplication.class, args);
}
}
The entities are the following:
Post.java
#Entity
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String content;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#Column(nullable = false)
private Timestamp createdAt;
#Column
private String location;
#OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Attachment> attachmentList;
#OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
private List<Rating> ratingList;
public Post() {
}
public Post(String content, User user, Timestamp createdAt, String location, List<Attachment> attachmentList, List<Rating> ratingList) {
super();
this.content = content;
this.user = user;
this.createdAt = createdAt;
this.location = location;
this.attachmentList = attachmentList;
this.ratingList = ratingList;
}
// ...
}
Attachment.java
#Entity
public class Attachment implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Lob
#Column(length = 100_000, nullable = false)
private byte[] content;
#ManyToOne
#JoinColumn(name = "post_id")
private Post post;
public Attachment() {
}
public Attachment(byte[] content, Post post) {
super();
this.content = content;
this.post = post;
}
// ...
}
User.java
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
#Column(nullable = false)
private Date dateOfBirth;
#Column(nullable = false)
private String credential;
#Column(nullable = false)
private String password;
#Column
private String location;
#Lob
#Column(length = 100_000)
private byte[] photo;
#Column
private String motto;
public User() {
}
public User(String firstName, String lastName, Date dateOfBirth, String credential, String password,
String location, byte[] photo, String motto) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = dateOfBirth;
this.credential = credential;
this.password = password;
this.location = location;
this.photo = photo;
this.motto = motto;
}
// ...
}
All repositories extend CrudRepository and are annotated with #Transactional:
PostRepository.java
#Transactional
public interface PostRepository extends CrudRepository<Post, Long> {
}
AttachmentRepository.java
#Transactional
public interface AttachmentRepository extends CrudRepository<Attachment, Long> {
}
UserRepository.java
#Transactional
public interface UserRepository extends CrudRepository<User, Long> {
}
The controller that should add a post to the feed is the following:
#Controller
#RequestMapping("/post")
public class PostController {
#Autowired
PostRepository postRepository;
#GetMapping("/add")
public String greetingForm(Model model) {
model.addAttribute("post", new Post());
return "addPost";
}
#PostMapping("/add")
public String addPost(#ModelAttribute Post post, #RequestParam("attachment") MultipartFile uploadingFile) throws IOException {
User user = new User();
user.setId(1L);
post.setUser(user);
post.setCreatedAt(Timestamp.valueOf(LocalDateTime.now()));
List<Attachment> attachmentList = new ArrayList<>();
Attachment attachment = new Attachment();
attachment.setContent(uploadingFile.getBytes());
attachment.setPost(post);
attachmentList.add(attachment);
post.setAttachmentList(attachmentList);
List<Rating> ratingList = new ArrayList<>();
post.setRatingList(ratingList);
postRepository.save(post);
return "allPosts";
}
}
The addPost.html page has the following content:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Post</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Add Post</h1>
<form action="#" th:action="#{/post/add}" th:object="${post}" method="post" enctype="multipart/form-data">
<table border="0">
<tr>
<td>Content</td>
<td><textarea id="content" th:field="*{content}" rows="5" cols="50"></textarea></td>
</tr>
<tr>
<td>Location</td>
<td><input type="text" id="location" th:field="*{location}"/></td>
</tr>
<tr>
<td>Attachment</td>
<td><input type="file" id="attachment" name="attachment"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</td>
</tr>
</table>
</form>
</body>
</html>
The application.properties file has the following content:
spring.datasource.url=jdbc:mysql://localhost:3306/****?useSSL=false
spring.datasource.username=root
spring.datasource.password=****
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.id.new_generator_mappings=false
spring.jpa.show-sql=true
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
However, when I press the submit button, nothing is persisted into the database, although the queries are being displayed in the console:
Hibernate: insert into post (content, created_at, location, user_id) values (?, ?, ?, ?)
Hibernate: insert into attachment (content, post_id) values (?, ?)
What could be the cause?
I am trying to select two columns which are in two separate tables. One table is users and other one is privillages. I need to fetch username from users and pname from privillages. My model classes are like follows,
#Entity
#Table(name = "users")
public class Users implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
public String username;
public String password;
public Integer privid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "pid")
private Collection<Privillages> priviJoin;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(name = "privid")
public Integer getPrivid() {
return privid;
}
public void setPrivid(Integer privid) {
this.privid = privid;
}
public Collection<Privillages> getPriviJoin() {
return priviJoin;
}
public void setPriviJoin(Privillages priviJoin) {
this.priviJoin = (Collection<Privillages>) priviJoin;
}
public Users() {
}
#Override
public String toString() {
return String.format("Users[id=%d, username='%s', password='%s']", id,
username, password);
}
}
And my Privillages class is,
#Entity
#Table(name = "privillages")
public class Privillages implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Integer id;
public String pname;
#ManyToOne(optional = false)
#JoinColumn(name = "pid", referencedColumnName = "privid")
public Users pid;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "pname")
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
#Column(name = "pid")
public Users getPid() {
return pid;
}
public void setPid(Users pid) {
this.pid = pid;
}
public Privillages(){
}
}
And my Users repository is,
public interface UsersRepository extends CrudRepository<Users, Integer>
{
#Query("select u from Users ug join ug.priviJoin u")
List<Users> findByUsername();
}
And My Privillage repository is:
public interface PrivillagesRepository extends CrudRepository<Privillages,
Integer> {
}
My controller file is:
#RequestMapping(value = "/joinResult", method = RequestMethod.GET)
public ModelAndView joinResultShow(Model model)
{
List<Privillages> privillages = (List<Privillages>)privillagesRepo.findAll();
model.addAttribute("joinData",privillages);
ModelAndView viewObj = new ModelAndView("fleethome");
return viewObj;
}
And displaying like:
<table>
<tr th:each="message : ${joinData}">
<td th:text="${message.pname}"></td>
<td th:text="${message.pid.username}"></td>
</tr>
</table>
And getting error like:
There was an unexpected error (type=Internal Server Error, status=500).
No message available
Stacktrace is:
java.lang.NullPointerException: null
at
com.central.controller.WebController.joinResultShow(WebController.java:58) ~
[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~
[na:1.8.0_141]
at sun.reflect.NativeMethodAccessorImpl.invoke
(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_141]
at sun.reflect.DelegatingMethodAccessorImpl.
invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_141]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_141]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke
(InvocableHandlerMethod.java:205) ~[spring-web-
4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.
invokeForRequest(InvocableHandlerMethod.java:133) ~[spring-web-
4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.
ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.
java:97) ~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.
RequestMappingHandlerAdapter.invokeHandlerMethod
(RequestMappingHandlerAdapter .java:827)
~[spring-webmvc-4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.
annotation.RequestMappingHandlerAdapter.handleInternal
(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-
4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.
handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-
4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.
doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-
4.3.11.RELEASE.jar:4.3.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService
(DispatcherServlet.java:901) ~[spring-webmvc-
4.3.11.RELEASE.jar:4.3.11.RELEASE]
I need to retrieve username from users and pname column from privillages. How should I change my code?
First of all, you should create privillages repository:
public interface PrivillagesRepository extends CrudRepository<Privillages, Integer> {
}
Then you can do find all privillages in your controller:
List<Privillages> privillages = privillages.findAll();
model.addAttribute("joinData",privillages);
After that, change your template:
<table>
<tr th:each="message : ${joinData}">
<td th:text="${message.pname}"></td>
<td th:text="${message.pid.username}"></td>
</tr>
</table>
As you can see, to print username, you need to use nested field "pid" (Users class)
I just explored the context that, Use the same repository as the Userrepository along with the JPQL query. And modify the view file according to that for displaying username with using the pid from Privillages Model class. I am adding the code need to modify with certain changes.
My controller file is,
UsersRepository userRepo;
#RequestMapping(value = "/joinResult", method = RequestMethod.GET)
public ModelAndView joinResultShow(Model model)
{
List<Users> use = (List<Users>) userRepo.findByUsername();
model.addAttribute("joinData",use);
ModelAndView viewObj = new ModelAndView("fleethome");
return viewObj;
}
And the repository ,
public interface UsersRepository extends CrudRepository<Users, Integer>
{
Users findByUsernameAndPassword(String username,String password);
#Query("select u from Users ug join ug.priviJoin u")
List<Users> findByUsername();
}
And My Privillage repository,
public interface PrivillagesRepository extends CrudRepository<Privillages,
Integer> {
}
And modify the View file with,
<table>
<th> Username </th>
<th> Privillage Name </th>
<tr th:each="message : ${joinData}">
<td th:text="${message.pid.username}"></td>
<td th:text="${message.pname}"></td>
</tr>
</table>
I'm trying to make a many-to-many example app with spring mvc and hibernate.
I have 2 classes, book and author. I've mapped these 2 class created all relations, but the problem is I can't add values to my join table.
Where's the problem?
CREATE TABLE author
(
author_id serial NOT NULL,
author_name character(100),
CONSTRAINT author_pkey PRIMARY KEY (author_id)
)
CREATE TABLE book
(
book_id serial NOT NULL,
book_name character(50) NOT NULL,
price integer NOT NULL,
CONSTRAINT book_pkey PRIMARY KEY (book_id)
)
CREATE TABLE book_author
(
book_id integer NOT NULL,
author_id integer NOT NULL,
CONSTRAINT book_author_pkey PRIMARY KEY (book_id, author_id),
CONSTRAINT book_author_author_id_fkey FOREIGN KEY (author_id)
REFERENCES author (author_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT book_author_book_id_fkey FOREIGN KEY (book_id)
REFERENCES book (book_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
and my java files
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int book_id;
#Column
private String book_name;
#Column
private int price;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "book_author",
joinColumns = { #JoinColumn(name = "book_id")},
inverseJoinColumns = { #JoinColumn(name = "author_id") }
)
private Set<Author> authors = new HashSet<Author>();
//geters and setters
}
Author.java
#Entity
#Table(name="author")
public class Author {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="author_id")
private int author_id;
#Column
private String author_name;
#ManyToMany(mappedBy = "authors")
private Set<Book> books = new HashSet<Book>();
//getters and setters
}
BookDAOImpl.java
#Repository
public class BookDAOImpl implements BookDAO{
#Autowired
private SessionFactory sessionFactory;
public void addBook(Book book) {
sessionFactory.getCurrentSession().saveOrUpdate(book);
}
}
BookServiceImpl.java
#Service
#Transactional
public class BookServiceImpl implements BookService{
#Autowired
private BookDAO bookDAO;
#Override
public void addBook(Book book) {
bookDAO.addBook(book);
}
}
BookController.java
#Controller
#SessionAttributes("authors")
public class BookController {
#Autowired
private BookService bookService;
#Autowired
private AuthorService authorService;
#RequestMapping(value = "/newBook", method = RequestMethod.GET)
public ModelAndView newBook(ModelAndView model) {
Book book = new Book();
model.addObject("book", book);
model.setViewName("bookForm");
return model;
}
#RequestMapping(value = "/saveBook", method = RequestMethod.POST)
public ModelAndView saveBook(#ModelAttribute Book book,BindingResult result)
{
if (book.getBook_id() == 0) {
// if employee id is 0 then creating the
// employee other updating the employee
bookService.addBook(book);
} else {
bookService.updateBook(book);
}
return new ModelAndView("redirect:/newBook");
}
#ModelAttribute("authors")
public List<Author> getAuthors(){
return authorService.getAuthors();
}
}
and finally here is bookForm.jsp file
<body>
<div align="center">
<h1>New/Edit Book</h1>
<form:form action="saveBook" method="post" modelAttribute="book">
<table>
<form:input type="hidden" path="book_id"/>
<tr>
<td>Name:</td>
<td><form:input path="book_name" /></td>
</tr>
<tr>
<td>Price:</td>
<td><form:input path="price" /></td>
</tr>
<tr>
<td>Author:</td>
<td><form:select path="authors" items="${authors}" itemValue="author_id" itemLabel="author_name"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="Save"></td>
</tr>
</table>
</form:form>
</div>
</body>
After the page opens, I enter some book name and price and choose an author and click the Save button. It saves book but not saves the join table book_author.
As you can see I can send author_id from uipng
And I can insert book object to database
In my current spring project, my forms are implement with a structure like this:
<form class="form" id="Pagina" role="form" method="POST" action="/loja/Pagina/cadastra" enctype="multipart/form-data">
...
</form>
and it's processed in server by this methos:
controller
#RequestMapping(value="cadastra", method=RequestMethod.POST)
#ResponseBody
#ResponseStatus(HttpStatus.CREATED)
public E cadastra(#ModelAttribute("object") E object, BindingResult result, #RequestParam(value="file", required=false) MultipartFile file, #RequestParam(value="icone", required=false) MultipartFile icone, #RequestParam(value="screenshot", required=false) MultipartFile screenshot[]) throws Exception {
E ret = serv.cadastra(object, file, icone, screenshot);
if (ret != null)
return ret;
else
throw new Exception();
}
service
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
#Transactional
public E cadastra(E e, MultipartFile file, MultipartFile icone, MultipartFile[] screenshot) {
return dao.persist(e);
}
My problem it's when the form have a field like this:
<label>pagina</label>
<select name="pagina.id" class="form-control select" data-lista="/loja/Pagina/listagem.json">
...
</select>
<label>produto</label>
<select name="produto.id" class="form-control select" data-lista="/loja/Produto/listagem.json">
...
</select>
which maps a atribute like this in the entiy class:
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="pagina_mae", nullable = true)
#Order(value=5)
#Select(name="pagina", ordem = 5)
#Sidebar
private Pagina pagina;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="produto_mae", nullable = true)
#Order(value=6)
#Select(name="produto", ordem = 6)
#Sidebar
private Produto produto;
Where the options inside looks like this:
<option value="">.</option>
<option value="...">...</option>
If I submit the form with the blank option selected, I get this error:
object references an unsaved transient instance - save the transient instance before flushing: com.spring.loja.model.pagina.persistence.model.Pagina; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.spring.loja.model.pagina.persistence.model.Pagina
but if, for instance, insert a record manually in the database (in my case, using pgAdmin3), and select this item in the select, the form is submitted without errors.
Anyone can tell me how I fix that, to allow me submit the form with or without selected data from the <select>.
UPDATE
code for the class Pagina:
#Entity
#Table(name="pagina")
#MainForm(grupo = 2, icone = "file")
public class Pagina extends ModelEntity {
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "nome", unique = true)
#Order(value=1)
#Input(type="hidden", name="nome", ordem = 1)
private String nome;
#Column(name = "titulo", nullable = false)
#Order(value=2)
#Input(name="titulo", ordem = 2)
private String titulo;
#Column(name = "descricao", length=65535)
#Order(value=4)
#Textarea(name="descricao", ordem = 4)
private String descricao;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="pagina_mae", nullable = true)
#Order(value=5)
#Select(name="pagina", ordem = 5)
#Sidebar
private Pagina pagina;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="produto_mae", nullable = true)
#Order(value=6)
#Select(name="produto", ordem = 6)
#Sidebar
private Produto produto;
}
UPDATE 2
PaginaEditor.java
#Component
public class PaginaEditor extends PropertyEditorSupport {
#Inject
private PaginaService paginaService;
#Override
public void setAsText(String text) {
if (!text.isEmpty()) {
Pagina pagina = paginaService.getObject(text);
setValue(pagina);
}
}
}
method added to my controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Pagina.class, new PaginaEditor());
}
Selects are tricky ones in Spring MVC.
I think your problem is that when your main entity is getting to the data layer to be persisted, the relationship is not there.
Try to debug and check if affirmation above is true.
There are two ways to get this sorted.
Let's assume a Company / Contact relationship in a contacts system.
A company has many contacts and a contact has one company.
Company snippet.
// package declaration imports and all
#Entity
#Table(name = "company")
public class Company {
private String name;
#OneToMany(mappedBy = "company")
private List<Contact> contacts = new ArrayList<Contact>();
// getter and setters and any extra stuff you fancy putting here
}
Contact snippet
// package declaration imports and all
#Entity
#Table(name = "contact")
public class Contact {
private String name;
#ManyToOne
private Company company;
// getter and setters and any extra stuff you fancy putting here
}
And a jsp snippet with the select.
We assume there is a "contact" object and a list of customers in the model.
<form:form modelAttribute="contact">
<form:input path="name" />
<form:select path="customer" items="${customers}" itemValue="id" itemLabel="name" />
</form:form>
With this code in place, you can use a PropertyEditor like this.
#Component
public class CustomerEditor extends PropertyEditorSupport {
#Inject
private CustomerService customerService;
#Override
public void setAsText(String text) {
if (StringUtils.isNotBlank(text)) {
Customer customer = this.customerService.findById(Integer
.valueOf(text));
this.setValue(customer);
}
}
}
and register in your Spring Context like this.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Customer.class, this.customerEditor);
}
What happens here is that, whenever Spring finds an Object of type Customer, it will use the PropertyEditor to convert the (in this case) Id to the object and by the type the contact (in this case) gets to the data layer (Hibernate), the proper Customer entity will be there waiting as happy as Larry.
That is automagic way to do it.
Another way to do it is to create a form/DTO or whatever you would like to call and add the fields including a customerId field (in this case) and convert your self before you save your entity.
I hope I understood your problem right because it took me quite a few minutes to write this... :)
Simply example, check it
Entity (USER, MOBILEPHONE)
#Entity
#Table(name = "USER")
public class User {
private Long id
private String name;
private Set<Mobilephone> mobilephones= new HashSet<mobilephones>(0);
public User(Long id)
this.id = id
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
//getter and setter for name
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
public Set<Mobilephone> getMobilephones() {
return this.mobilephones;
}
public void setMobilephones(Set<Mobilephone> mobilephones) {
this.mobilephones= mobilephones;
}
#Entity
#Table(name = "MOBILEPHONE")
public class Mobilephone {
private Long id
private Long number;
private User user
public MobilePhone(Long id)
this.id = id
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
//getter and setter for number
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USERID", nullable = false)
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user= user;
}
Webpage
<form:form modelAttribute="mobilephoneAttribute" action="url" method="post">
<form:input path="mobilephone"/>
<form:select path="user">
<c:forEach items="${userlist}" var="user">
<form:option value="${user.id}" label="${user.telephone" />
</c:forEach>
</form:select>
<input type="submit"/>
</form:form>
Whats happening.
After submit i get this error:
The request sent by the client was syntactically incorrect.
If I change my User: "Long id" to "String id" (and also methods) the problem disappears.
I thought at the beginning, spring has a problem with the convert Long to String?
But probably not, because we have a number where Long is saved with no problems.
Someone knows the problem?
try using
<form:select path="user.id">
<c:forEach items="${userlist}" var="user">
<form:option value="${user.id}" label="${user.telephone" />
</c:forEach>
</form:select>