Spring MVC <form:options> selected value - select

I got relation "Many to one" between my CPVCode adn OrderType:
public class CPVCode {
#Id
#GeneratedValue
private int id;
private String cpv_code;
private String description;
#ManyToOne
#JoinColumn(name="id_parent")
private OrderType orderType;
//getters na setters: ...
}
Everything works well, but I NEED to displays selected value in my form:
<form:select path="orderType" items="${orderTypes }" itemLabel="title" itemValue="id" ></form:select>
It seems to work almost good: It displays list of all OrderTypes ( by ${orderTypes} which returns array of that objects type), it saves proper values by Hibernate, BUT thereis no way to select current value of orderType after refreshing...

your passing a list to a selectbox, so it iterates over the list. You need to change which bean the selectbox references - a single value oderType from CPVcode.
And also possibly change the selectbox to a different html form element ?

Related

How to accept input for a list of objects using checkboxes in a form

I have a scenario with 2 entities:
1)Student:
#Entity
#Table(name = "student_registration")
public class Student
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String firstName;
#Column
private String lastName;
#OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
private List<Hobby> hobbies;
//constructors (default, all params), getters and setters
}
2)Hobbies:
#Entity
#Table(name = "hobby")
public class Hobby
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "hobby_id")
private Student student;
#Column
private String name;
//contructors (default, Hobby(name, student), getters and setters)
}
I want to input each checkbox option from a group of checkboxes as a hobby with name equal to the value of the checkbox
Here is my JSP section for the above:
<div class="form">
<form:form action="success" modelAttribute="student" onsubmit="return validate()">
<div class="row">
<div class="label">First Name</div>
<div class="field">
<form:input path="firstName" />
</div>
</div>
<div class="row">
<div class="label">Last Name</div>
<div class="field">
<form:input path="lastName" />
</div>
</div>
<div class="row">
<div class="label">Hobbies</div>
<div class="field">
<form:checkbox path="hobbies" id="hobby1Checkbox" value="Hobby 1" />
<label class="sublabel" for="hobby1Checkbox">Hobby 1</label>
<form:checkbox path="hobbies" id="hobby2Checkbox" value="Hobby 2" />
<label class="sublabel" for="hobby2Checkbox">Hobby 2</label>
<form:checkbox path="hobbies" id="hobby3Checkbox" value="Hobby 3" />
<label class="sublabel" for="hobby3Checkbox">Hobby 3</label>
</div>
</div>
</form:form>
</div>
Here is my controller:
#Controller
public class StudentController {
#Autowired
StudentService stuServ;
#RequestMapping("/")
public String index() {
return "index";
}
#GetMapping("/registration")
public String registrationRedirect(ModelMap studentModel)
{
studentModel.addAttribute("student", new Student());
studentModel.addAttribute("hobby", new Hobby());
return "registration";
}
#PostMapping("/success")
public String sayHello(#ModelAttribute("registeredStudent") Student student) {
stuServ.addStudent(student);
return "success";
}
}
I understand that what I have done is incorrect, since I cannot map a String value from a checkbox to a list of objects (List)
However I am unable to find a way to achieve the same. I was looking into possible solutions using a Converter or a PropertyEditor but I am new to Spring MVC and I'm confused.
*Update:
I have observed that upon adding a parameterized constructor within the Hobby class with only the name as the argument, I am able to directly pass the value to the Hobby object using the checkboxes, since the String value of the checkboxes is initialized to the Hobby name via the aforementioned contructor.
This works if I am only to use the hobby name for my registration success view, however another obvious issue is that the student field of the Hobby class never gets initialized since only the contructor Hobby(name) is getting invoked, hence the relationship isn't bidirectional. Student field remains null in the Hobby class.
Kindly assist with appropriate ways to handle the situation. I am unable to think of ways with which I might let Spring know that which student am I assigning the Hobby selected by the checkbox to.
Edit with respect to my earlier provided answer:
Another issue arises if I create a repository for Hobby entity:
public interface HobbyRepository extends JpaRepository<Hobby, Integer>
{
boolean findByName(String hobby);
}
The checkbox String value initialization for hobbies does not work anymore. Earlier the constructor with name parameter for Hobby entity did the job but on addition of Hobby repository I am getting the following expected error:
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='registeredStudent'. Error count: 1
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'registeredStudent' on field 'hobbies': rejected value [Singing,Sketching,Swimming]; codes [typeMismatch.registeredStudent.hobbies,typeMismatch.hobbies,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registeredStudent.hobbies,hobbies]; arguments []; default message [hobbies]]; default message [Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.List' for property 'hobbies'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value 'Singing'; nested exception is java.lang.NumberFormatException: For input string: "Singing"]
Pretty much, the Hobby with name = the String value of the checkboxes isn't being able to initialize anymore. I have tried removing the Hobby repository and it works perfectly. Any further insight would be appreciated as the only possible solution for now appears to be adding an extra List<String> field in my Student POJO class and using the values from checkboxes passed in the field to initialize the Hobby objects contained in Student.hobbies
In my controller, the current student object passed as ModelAttribute can be assigned to each of the hobbies belonging to the current student object, thereby completing the bidirectional relationship, as follows:
#PostMapping("/success")
public String sayHello(#ModelAttribute("registeredStudent") Student student) {
for(Hobby h : student.getHobbies())
{
h.setStudent(student);
}
stuServ.addStudent(student);
return "success";
}
I had to exclude the mapped fields, i.e. hobbies in Student entity and student in Hobby entity from the hashcode and equals methods in the respective entities, in order to prevent StackOverFlow Error due to infinite nesting of the entities
This works as a solution, however there might be better ways to approach this problem.
Edit:
Another issue arises if I create a repository for Hobby entity:
public interface HobbyRepository extends JpaRepository<Hobby, Integer>
{
boolean findByName(String hobby);
}
The checkbox String value initialization for hobbies does not work anymore. Earlier the constructor with name parameter for Hobby entity did the job but on addition of Hobby repository I am getting the following expected error:
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='registeredStudent'. Error count: 1
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'registeredStudent' on field 'hobbies': rejected value [Singing,Sketching,Swimming]; codes [typeMismatch.registeredStudent.hobbies,typeMismatch.hobbies,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registeredStudent.hobbies,hobbies]; arguments []; default message [hobbies]]; default message [Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.List' for property 'hobbies'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value 'Singing'; nested exception is java.lang.NumberFormatException: For input string: "Singing"]
Pretty much, the Hobby with name = the String value of the checkboxes isn't being able to initialize anymore. I have tried removing the Hobby repository and it works perfectly. Any further insight would be appreciated as the only possible solution for now appears to be adding an extra List<String> field in my Student POJO class and using the values from checkboxes passed in the field to initialize the Hobby objects contained in Student.hobbies

Is JPA Embeddable a Value Object and Why Can't it Represent a Table

PROBLEM: I have read-only data in a table. Its rows have no id - only composite key define its identity. I want it as a Value Object (in DDD terms) in my app.
RESEARCH: But if I put an #Embeddable annotation instead of #Entity with #Id id field, then javax.persistence.metamodel doesn't see it and says Not an embeddable on Metamodel metamodel.embeddable(MyClass.class);. I could wrap it with an #Entity class and autogenerate id, but this is not what I architectually intended to achieve.
QUESTION: Is JPA Embeddable a Value Object? Can Embeddable exist without a parent Entity and represent a Table?
There are many articles on the topic that show this is a real JPA inconvenience:
http://thepaulrayner.com/persisting-value-objects/
https://www.baeldung.com/spring-persisting-ddd-aggregates
https://paucls.wordpress.com/2017/03/04/ddd-building-blocks-value-objects/
https://medium.com/#benoit.averty/domain-driven-design-storing-value-objects-in-a-spring-application-with-a-relational-database-e7a7b555a0e4
Most of them suggest solutions based on normalised relational database, with a header-entity as one table and its value-objects as other separate tables.
My frustration was augmented with the necessity to integrate with a non-normalized read-only table. The table had no id field and meant to store object-values. No bindings with a header-entity table. To map it with JPA was a problem, because only entities with id are mapped.
The solution was to wrap MyValueObject class with MyEntity class, making MyValueObject its composite key:
#Data
#Entity
#Table(schema = "my_schema", name = "my_table")
public class MyEntity {
#EmbeddedId MyValueObject valueObject;
}
As a slight hack, to bypass JPA requirements for default empty constructor and not to break the immutability of Value Object, we add it as private and sacrifice final modifier for fields. Privacy and absence of setters conforms the initial DDD idea of Value Object:
// #Value // Can't use, unfortunately.
#Embeddable
#Immutable
#AllArgsConstructor
#Getter
#NoArgsConstructor(staticName = "private") // Makes MyValueObject() private.
public class MyValueObject implements Serializable {
#Column(name = "field_one")
private String myString;
#Column(name = "field_two")
private Double myDouble;
#Transient private Double notNeeded;
}
Also there is a handful Lombok's #Value annotaion to configure value objects.

Spring Data JPA: Work with Pageable but with a specific set of fields of the entity

I am working with Spring Data 2.0.6.RELEASE.
I am working about pagination for performance and presentation purposes.
Here about performance I am talking about that if we have a lot of records is better show them through pages
I have the following and works fine:
interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {
}
The #Controller works fine with:
#GetMapping(produces=MediaType.TEXT_HTML_VALUE)
public String findAll(Pageable pageable, Model model){
Through Thymeleaf I am able to apply pagination. Therefore until here the goal has been accomplished.
Note: The Persona class is annotated with JPA (#Entity, Id, etc)
Now I am concerned about the following: even when pagination works in Spring Data about the amount the records, what about of the content of each record?.
I mean: let's assume that Persona class contains 20 fields (consider any entity you want for your app), thus for a view based in html where a report only uses 4 fields (id, firstname, lastname, date), thus we have 16 unnecessary fields for each entity in memory
I have tried the following:
interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {
#Query("SELECT p.id, id.nombre, id.apellido, id.fecha FROM Persona p")
#Override
Page<Persona> findAll(Pageable pageable);
}
If I do a simple print in the #Controller it fails about the following:
java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to com.manuel.jordan.domain.Persona
If I avoid that the view fails with:
Caused by:
org.springframework.expression.spel.SpelEvaluationException:
EL1008E:
Property or field 'id' cannot be found on object of type
'java.lang.Object[]' - maybe not public or not valid?
I have read many posts in SO such as:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
I understand the answer and I am agree about the Object[] return type because I am working with specific set of fields.
Is mandatory work with the complete set of fields for each entity? Should I simply accept the cost of memory about the 16 fields in this case that never are used? It for each record retrieved?
Is there a solution to work around with a specific set of fields or Object[] with the current API of Spring Data?
Have a look at Spring data Projections. For example, interface-based projections may be used to expose certain attributes through specific getter methods.
Interface:
interface PersonaSubset {
long getId();
String getNombre();
String getApellido();
String getFecha();
}
Repository method:
Page<PersonaSubset> findAll(Pageable pageable);
If you only want to read a specific set of columns you don't need to fetch the whole entity. Create a class containing requested columns - for example:
public class PersonBasicData {
private String firstName;
private String lastName;
public PersonBasicData(String firstName, String lastName) {
this.firstName = fistName;
this.lastName = lastName;
}
// getters and setters if needed
}
Then you can specify query using #Query annotation on repository method using constructor expression like this:
#Query("SELECT NEW some.package.PersonBasicData(p.firstName, p.lastName) FROM Person AS p")
You could also use Criteria API to get it done programatically:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonBasicData> query = cb.createQuery(PersonBasicData.class);
Root<Person> person = query.from(Person.class);
query.multiselect(person.get("firstName"), person.get("lastName"));
List<PersonBasicData> results = entityManager.createQuery(query).getResultList();
Be aware that instance of PersonBasicData being created just for read purposes - you won't be able to make changes to it and persist those back in your database as the class is not marked as entity and thus your JPA provider will not work with it.

Hibernate Search and composed key using #IdClass

I have a problem to integrate Hibernate Search in existing project with hundreds of entities but at least half of entities use #IdClass annotation as composed key. Can I solve the problem using the annotation #IdClass?
I also read this post Hibernate search and composed keybut I have not managed to solve my problem.
I have the following example:
entity class:
#Entity
#Table(name="FAKVS_DB")
#IdClass(value=PK_FAKVS_DB.class)
#Audited
#Indexed
public class FAKVS_DB implements Serializable {
#Id
#Column(name="Key_FAM", length=10, nullable=false)l
private String keyFam;
#Id
#Column(name="Komponentennr", nullable=false)
private Integer komponentenNr;
#Id
#Column(name="Hinweis", nullable=true, length=4)
private String hinweis;
//getters and setters
}
and composed key:
public class PK_FAKVS_DB implements Serializable {
private String keyFam;
private Integer komponentenNr;
private String hinweis;
//getters and setters
}
The error that occurs is:
HSEARCH000058: HSEARCH000212: An exception occurred while the MassIndexer was transforming identifiers to Lucene Documents
java.lang.ClassCastException: package.entities.module.fi.pk.PK_FAKVS_DB cannot be cast to java.lang.Integer
at org.hibernate.type.descriptor.java.IntegerTypeDescriptor.unwrap(IntegerTypeDescriptor.java:36)
at org.hibernate.type.descriptor.sql.IntegerTypeDescriptor$1.doBind(IntegerTypeDescriptor.java:63)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:90)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:286)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:281)
at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:1995)
at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1966)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1901)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1862)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1839)
at org.hibernate.loader.Loader.doQuery(Loader.java:910)
If I can not use #IdClass annotation can you tell me what are the alternatives?
Thank you very much in advance.
An alternative is to add a new property to be used as Id by Hibernate Search. You can mark this with #DocumentId to have the Hibernate Search engine treat the alternative property as the identifier in the index.
You will need to ensure that this new property is unique of course; this can typically done by generating a String from the real id. You probably want to annotate the new getter with #Transient so that it doesn't get persisted in the database.

A strange phenomenon when use dozer in jpa project,why Mapping annotation in lazy load object can't work?

I met a very strange phenomenon when using dozer in jpa project.
I have a UserSupplier object and a Supplier object.
UserSupplier:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "supplier_id", nullable = false)
private Supplier supplier;
In my code I first query a UserSupplier List, then convert it to SupplierList.
List<Supplier> supplierList = new ArrayList<>(usList.size());
usList.forEach(us -> supplierList.add(us.getSupplier()));
Then I convert SupplierList to SupplierView List and return it to Caller.
BeanMapper.mapList(supplierList, SupplierView.class);
My dozer configure in these objects like below
Supplier:
#Id
#GeneratedValue
#Mapping("supplierId")
private int id;
SupplierView:
private int supplierId;
Very funny, supplierId in SupplierView always 0(default int value),but other fileds can convert successfully, only id field fail. I don't why is this, why only id field can't convert to supplierId, but other fields could?
For above problem, there are below solutions
1. Change field name (supplierId to id):
Supplier:
// #Mapping("supplierId")
private int id;
SupplierView:
private int id;
but Caller(front-end) have to change code.
2. Change fetchType to eager:
UserSupplier:
#ManyToOne
private Supplier supplier;
After reading dozer documentation, I find some thing. After trying it, I got another solution.
That is add a dozer.properties into classpath, content inside is
org.dozer.util.DozerProxyResolver=org.dozer.util.HibernateProxyResolver
More detail please see
http://dozer.sourceforge.net/documentation/proxyhandling.html
This is probably because JPA uses proxy objects for lazy loading of single entity reference. Proxy object is effectively a subclass of your entity class. I guess that dozer can find #Mapping annotation only on fields declared in the class of given object, and not on fields defined in parent classes. Dozer project states that annotation mapping is experimental. Therefore it is possible that it does not cover mapping class hierarchies well.
I suggest to try configure mapping of supplierId by other means (XML, dozer mapping API) and see if it works. If all fails, you could write a custom MapperAware converter between Supplier and SupplierView. You would map source object to target object using supplied mapper, and finilize it by copying value of id to supplierId.