Have to beans:
#Entity
#Table(name="book")
public class Book {
#Id
#Column(name="id_book")
#GeneratedValue(generator="increment")
#GenericGenerator(name="increment", strategy="increment")
private int id;
#Column
#Size(min=1,max=100)
private String title;
#Column
#Size(min=1,max=400)
private String description;
#Column
private Integer year=0;
#ManyToMany(cascade={CascadeType.ALL},fetch = FetchType.EAGER)
#Fetch (FetchMode.SELECT)
#JoinTable(name="book_author",
joinColumns={#JoinColumn(name="book_id_book")},
inverseJoinColumns= {#JoinColumn(name="author_id_author")})
private List<Author> author=new ArrayList<Author>();
//getters/setters
}
and:
#Entity
#Table(name="author")
public class Author {
#Id
#Column(name="id_author")
#GeneratedValue
private Integer id;
#Column
private String name;
#Column
private String surname;
#ManyToMany(mappedBy="author")
private Set<Book> book=new HashSet<Book>();
//getters/setters
}
In my jsp I'm have form for enter data about book, and multiple list for select author(s) from DB, problem only in select authors, therefore give only this code:
<sf:select multiple="true" path="author" items="${authors}" size="7" >
</sf:select>
Where ${authors} - List with objects Author from DB. Use POST request.
In my controller for this page have this (I know it's not correct):
#RequestMapping(value="/addbook", method=RequestMethod.POST)
public String addBook(Book book){
hibarnateService.saveBook(book);
return "redirect:/books";
}
When I'm create book without select authors, but enter another information, all fine, book save in DB. When select some authors get this - The request sent by the client was syntactically incorrect.
Problem solved by add in controller:
#InitBinder
protected void initBinder(WebDataBinder binder){
binder.registerCustomEditor(Author.class, new Editor(hibarnateService));
}
and create class:
public class Editor extends PropertyEditorSupport {
private final Dao hibernateService;
public Editor(Dao hibernateService){
this.hibernateService=hibernateService;
}
#Override
public void setAsText(String text) throws IllegalArgumentException{
Author author=hibernateService.getAuthor(Integer.parseInt(text));
setValue(author);
}
}
P.S. What wrong with me? I can't find the right answer myself until I ask here)
You will need to implement initBinder in your controller, below can be tentative code (not tested)
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(List.class, "authors ", new CustomCollectionEditor(List.class)
{
#Override
protected Object convertElement(Object element)
{
Long id = null;
if(element instanceof Long) {
//From the database 'element' will be a Long
id = (Long) element;
}
return id != null ? authorService.loadAuthorById(id) : null;
}
});
}
Related
Well, I'm using Hibernate for the first time and, unexpectedly, it works. Except for one thing: an insert with a pk already inserted overwrite the record instaed of preventing it.
That's my simple code:
#Controller
public class SimpleController {
#Autowired
UserRepository userRepository;
#GetMapping("/mainPage")
public String viewMainPage(){
return "mainPage";
}
#GetMapping("/nuovo-utente")
public String viewInserisciUtente(Model model){
model.addAttribute("nuovoUtente", new Utente());
return "nuovo-utente";
}
#PostMapping("/nuovo-utente")
public String memorizzaUtente(#ModelAttribute Utente utente){
userRepository.save(utente);
return "output";
}
}
#Entity
public class Utente {
#Id
private int id;
private String citta=null;
private String genere=null;
private String data_nascita=null;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCitta() {
return citta;
}
public void setCitta(String citta) {
this.citta = citta;
}
public String getGenere() {
return genere;
}
public void setGenere(String genere) {
this.genere = genere;
}
public String getData_nascita() {
return data_nascita;
}
public void setData_nascita(String data_nascita) {
this.data_nascita = data_nascita;
}
}
Any help will be appreciated.
EDIT: I've added the entity class to help you understanding my problem. Hoping that this will help.
Thanks you all
If you look at CrudRepository documentation, then we don't have update method, but we only have save method, which is used to add or update existing records.
In your case, you might have updated an entity (except its Id field) and tried saving the entity. So, CrudRepository will update the existing value for given Id, since it is already present.
Try adding ID generation strategy to id field.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
This question already has an answer here:
Binding child object on submit spring mvc
(1 answer)
Closed 5 years ago.
I am new at Hibernate/JPA and I am trying to get form parameter with hibernate entity class. There was no problem with it until when I tried to get parameter with Entity class that has relationship with other class. For example;
Controller:
#RequestMapping(value = "/addProduct", method = RequestMethod.POST)
public String addProduct(Model model, Product product) {
databaseService.insert(product);
return "redirect:/products";
}
Entity class:
#Entity
#Table(name = "products")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private String id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private Category category;
#Column(name = "name")
private String name;
#Column(name = "price")
private String price;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
Category class :
#Entity
#Table(name = "categories")
public class Category implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The program cannot set 'category'. Because category is not type like int, string.. I am realize the problem. But I cannot find solution to mapping parameters with Entity class. Is there any way to solve this. Or should I use #RequestParam to get parameters one-by-one instead of mapping parameters with entity class.
UPDATE
I just change category to category.id in my .jsp page and it solved my problem.
old code
<form>
...
<select class="form-control" name="category">
<c:if test="${not empty categoryList}">
<c:forEach var="item" items="${categoryList}">
<option value="${item.getId()}">${item.getName()}</option>
</c:forEach>
</c:if>
</select>
</form>
new code
<form>
...
<select class="form-control" name="category.id">
<c:if test="${not empty categoryList}">
<c:forEach var="item" items="${categoryList}">
<option value="${item.getId()}">${item.getName()}</option>
</c:forEach>
</c:if>
</select>
</form>
Please show us your form mapping,
Till then can could try with, change path in <form:select>/<form:input> tag to category.id and category.name
have a look at my another answer
I will suggest don't expose your Entity in the View, try to get form data in DTO, then convert to entity..
One way to do that is by creating a custom Spring Converter. So lets say you will be passing your entity's Id as a path variable, and your converter implementation would get that product object for you.
In your Controller you will need to do the following:
#RequestMapping(value = "/addProduct/{id}", method = RequestMethod.POST)
public String addProduct(Model model, #PathVariable("id") Product product) {
databaseService.insert(product);
return "redirect:/products";
}
Your Converter would look something like this:
import org.springframework.core.convert.converter.Converter;
public class StringToProductConverter implements Converter<String, Product> {
...
#Override
public Product convert(String id) {
Product product = databaseService.getProduct(id);
...
return product;
}
And don't forget to register your Converter either programmatically or by XML depending on your Spring version you're working on.
I'm creating Pet Clinic. If the user wants to add new pet he has to input Owner Id. How to check if this Id already exists. If no I want to use the redirect to the new form where the user can add new Owner.
<tr>
<td><label>Owner: </label></td>
<td><form:input path="ownerId"/></td>
</tr>
Edit:
I want to check if exists in the database. I have two tables in MySQL. Owner and Pet. Owner can have many Pets, Pet can have just one Owner.
Part of Pet Class:
#Entity
#Table(name="pet")
public class Pet {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="type")
private String type;
#Column(name="name")
private String name;
#Column(name="sickness")
private String sickness;
#Column(name="owner_id")
private String ownerId;
...
}
Owner class:
#Entity
#Repository
public class Owner {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="phone_number")
private String phoneNumber;
...
}
Controller
#Controller
#RequestMapping("/pet")
public class PetController {
#Autowired
private PetService petService;
#GetMapping("/list")
public String listPets(Model theModel){
List<Pet> thePets = petService.getPets();
theModel.addAttribute("pets", thePets);
return "list-pets";
}
...
#PostMapping("/savePet")
public String savePet(#ModelAttribute("pet") Pet thePet){
petService.savePet(thePet);
return "redirect:/pet/list";
}
}
Serive
#Service
public class PetServiceImpl implements PetService{
#Autowired
private PetDAO petDAO;
#Transactional
public List<Pet> getPets() {
return petDAO.getPets();
}
#Transactional
#Override
public void savePet(Pet thePet) {
petDAO.savePet(thePet);
}
}
DAO
#Repository
public class PetDAOImpl implements PetDAO {
#Autowired
private SessionFactory sessionFactory;
public List<Pet> getPets() {
Session currentSession = sessionFactory.getCurrentSession();
Query<Pet> theQuery = currentSession.createQuery("from Pet", Pet.class);
List<Pet> thePets = theQuery.getResultList();
return thePets;
}
#Override
public void savePet(Pet thePet) {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(thePet);
}
}
In JSP you can check if object has value with JSTL core lib:
<c:if test="${not empty pet.ownerId}">...</c:if>
where pet is your model object (passed to form).
My application has entities with nameEn and nameDe for english and german. But only english being used now. Since there are so many entities available, I wanted to have a generic class which can return the selected language entries,but for multiple entries my approach didn't work.
#Entity
#Table(name="employee")
public class Employee implements java.io.Serializable {
// Fields
private Integer id;
private String nameEn;
private String nameDe;
//Getter, Setter Methods
}
#Entity
#Table(name="address")
public class Address implements
java.io.Serializable {
private String descriptionEn;
private String descriptionDe;
}
public interface ILabelText {
String getNameEn();
String getNameDe();
String getDescriptionEn();
String getDescriptionDe();
}
public abstract class LabelText implements ILabelText, Serializable {
private static final long serialVersionUID = 1L;
protected String descriptionEn;
protected String descriptionDe;
private Logger log = Logger.getLogger(LabelText.class);
String language = FacesContext.getCurrentInstance().getViewRoot().getLocale().getLanguage();
public String getDescription() {
log.info("Language Selected is " + language);
if (language.equals("De")) {
return getDescriptionDe();
} else {
return getDescriptionEn();
}
}
public String getName() {
log.info("Language Selected is " + language);
if (language.equals("De")) {
return getNameDe();
} else {
return getNameEn();
}
}
}
//In Xhtml, based on selected locale, display value accordingly
<h:outputText value="#{emp.getName()}" />
<h:outputText value="#{add.getDescription()}" />
You can create an entity Lang like this
#Entity
public class Lang implements Serializable
{
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#NotNull
private String key;
#NotNull
private String translation;
}
and use it in your Address like this
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#MapKey(name = "key")
protected Map<String, Lang> name;
Then you are able to access the correct language in JSF:
<h:outputText value="#{emp.name[userLocale].translation}" />
The expression userLocale should be resolved to your language key (en, de, ...) or can be hardcoded e.g. #{emp.name['en'].translation}.
Is more easy you create a table with translations:
e.g:
People -> All of your persons
PersonTranslations
People | id
PersonTranslations | locale; person_id;
then on your Person class you set the language for all attributes on predicate
Person.description (this will search on PersonTranslation using a person_id key, and a locale)
some like that PersonTranslation.find(1, 'en');
I have an Entity Campaign that has a OneToOne relationship with CampaignCities cities.
In turn, CampaignCities contains a Set cities;
The campaign entity
#Entity
#javax.persistence.Table(uniqueConstraints={#UniqueConstraint(columnNames={"name","company_id"}), #UniqueConstraint(columnNames={"id"})})
public class Campaign implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Long id;
#NotEmpty
#Size(min=1, max=100)
private String name;
private Date startDate;
private Date endDate;
#Valid
private Deal deal;
#Valid
private Company company;
#OneToOne
private CampaignCities cities = new CampaignCities();
The CampaignCities entity
#Entity
public class CampaignCities {
private long id;
private Set<City> cities = new HashSet<City>();
#Id
#javax.persistence.GeneratedValue(strategy=GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#OneToMany
public Set<City> getCities() {
return cities;
}
public void setCities(Set<City> cities) {
this.cities = cities;
}
}
The City entity:
#Entity
public class City implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private Long id;
#javax.persistence.Id
#javax.persistence.GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My NewCampaignController
#SessionAttributes(value={"campaign", "campaignCities"})
#Controller
public class NewCampaignController {
//private static final Logger logger = LoggerFactory.getLogger(NewDealController.class);
#Autowired
private CampaignManager campaignManager;
#Autowired
private CityManager cityManager;
#Autowired
SimpleDateFormat dateFormat;
#Autowired
CustomDateEditor dateEditor;
#RequestMapping(value = "campaign/new", method = RequestMethod.GET)
public String showForm(Map<String, Object> model) {
//List<Campaign> campaigns = campaignManager.getCampaigns();
Campaign campaignForm = new Campaign();
CampaignCities cities = new CampaignCities();
cities.setCities(new HashSet<City>(cityManager.getCity()));
//campaignForm.setCities(cities);
model.put("campaignCities", cities);
model.put("campaign", campaignForm);
return "campaign/new";
}
#RequestMapping(value = "campaign/new", method = RequestMethod.POST)
public String processForm(#Valid Campaign campaignForm, BindingResult result, Map<String,Object> model) {
new CampaignValidator().validate(campaignForm, result);
if (result.hasErrors()) {
return "campaign/new";
}
this.campaignManager.saveCampaign(campaignForm);
model.put("campaign", campaignForm);
model.put("campaigns", this.campaignManager.getCampaigns());
return "campaign/added";
}
I have been able to get campaign to render in a form and I've rendered the list of cities successfully using:
<form:checkboxes items="${campaignCities.cities}" path="cities" itemLabel="name" itemValue="id" delimiter="<br/>" />
However when i submit the form, I get the following validation error.
Field error in object 'campaign' on field 'cities': rejected value
[2,1]; codes
[typeMismatch.campaign.cities,typeMismatch.cities,typeMismatch.com.groupdealclone.app.domain.CampaignCities,typeMismatch];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [campaign.cities,cities]; arguments []; default message
[cities]]; default message [Failed to convert property value of type
'java.lang.String[]' to required type
'com.groupdealclone.app.domain.CampaignCities' for property 'cities';
nested exception is java.lang.IllegalStateException: Cannot convert
value of type [java.lang.String[]] to required type
[com.groupdealclone.app.domain.CampaignCities] for property 'cities':
no matching editors or conversion strategy found]
I've tried to figure out a way to handle this in SpringMVC 3 but I've been stuck for over a day with no success. I simply want a List or Set or Cities that where checked on the form to be submitted to the controller and added to the Campaign. How do I get around the conversion problem where I can convert the String[] returned to a List or Set of Cities.
The project I'm working on is a public GitHub project, you can download the source and set it up using Maven if you like the project is called Group-Deal-Clone
After what is almost 2 days, the answer was simpler than I expected. Thanks to this thread I was guided to the answer.
In my NewCampaignController I did:
#InitBinder
public void initBinder(WebDataBinder binder) {
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, dateEditor);
binder.registerCustomEditor(CampaignCities.class, "cities", new PropertyEditorSupport() {
#Override
public void setAsText(String text) {
String [] ids = text.split(",");
CampaignCities cities = null;
for(String id:ids){
if(cities == null)
cities = new CampaignCities();
City city = cityManager.getCity(new Long(id));
if(city != null)
cities.getCities().add(city);
}
if(cities != null){
cities.setId(null);
setValue(cities);
}
}
});