How to use hidden field to store data model in wicket - wicket

I have a entity, name Product.It have two property is unit (byte) and unitName(String). unit property is mapped on database. Ex: 0:Kg ; 1:g;.... I want when input a valid unit, unit property is stored; unless, it save to unitName
Product
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id")
private int productId;
#Column(name = "product_name")
private String productName;
#Column(name = "unit")
private Byte unit;
#Transient
private String unitName;
}
In unit text field, I use a UnitConvert
UnitConvert
public class UnitConverter implements IConverter<Byte> {
private static final long serialVersionUID = 4798262219257031818L;
public UnitConverter() {
}
#Override
public Byte convertToObject(String value, Locale locale) {
return Text.isEmpty(value) ? 0 : UtilCommon.getTaniCode(value);
}
#Override
public String convertToString(Byte value, Locale locale) {
return (value == null || value==0 ) ? "" : UtilCommon.getTaniName(value);
}
}
I only think about HiddenField to do that, but I don't know how to do that.
Someone know how to use or anything can help me. Thank you very much

So from what I understood you want to save the input of a Model to a different database property depending on certain checks before hand. You can do that in your Form.onSubmit() method.
A very simple implementation could look like this:
public ProductPanel(String id, final IModel<Object> productModel) {
super(id, productModel);
// we're putting the productModel into the constructor.
// Therefore it's guaranteed to be detached
// -> it's okay to have it with final modifier.
IModel<String> formModel = Model.of("");
Form<String> form = new Form<String>("form", formModel) {
#Override
protected void onSubmit() {
super.onSubmit();
String productName = getModelObject();
Object realProduct = productModel.getObject();
if (isAcceptableUnit(productName)) {
realProduct.setUnit(parseUnit(productName));
} else {
realProduct.setUnitName(productName);
}
layer.saveProduct();
}
};
add(form);
TextField<String> productName = new TextField<String>("textField", formModel);
form.add(productName);
}
private boolean isAcceptableUnit(String productName) {
// your logic to determine if it's okay to cast to byte here...
return true;
}
private byte parseUnit(String productName) {
// your logic to parse the String to byte here...
return 0;
}
Some additional comments since I'm uncertain if the code snippets you provided are just for simplicity or actually code pieces:
You should try to avoid declaring your db object Serializable. Should you use normal Model objects to save your DTOs wicket will actually serialize them and you won't be able to do anything with them (well with hibernate at least).
Database object should use LoadableDetachableModel and save the primary key to load the entity in the load() method of it.
This would enable you now to work directly on those objects by using CompoundPropertyModel etc (which has it's pros and cons which I will not explain in detail here).
Still in your case I would add an Model<String> to the form and let the server decide how the input should be handled and mapped to the actual domain object.

Related

Spring Data Rest testing with JPA Relationship Mapping : Odd behaviour with update

I followed this tutorial (https://spring.io/guides/tutorials/react-and-spring-data-rest/#react-and-spring-data-rest-part-5) to experiment Spring Data REST and I wanted to test the CRUD with TestRestTemplate.
Add (postForEntity) is ok.
Delete (delete) is ok.
Read (getForEntity) is ok.
Update (template.exchange(URL, HttpMethod.PUT, entity, String.class, ID)) only works when I don't have any relation with other entities... and I don't understand why.
Here's an example :
#Data
#Entity
public class Dojo {
private #Id #GeneratedValue Long id;
private String name;
private String location;
private Date created;
#OneToMany(mappedBy = "dojo")
#JsonIgnore
private List<Workshop> workshops;
private Dojo() {}
public Dojo(String name, String location) {
this.name = name;
this.location = location;
this.created = new Date();
this.workshops = new ArrayList<>();
}
//getters and setters ...
}
#Data
#Entity
public class Workshop {
private #Id #GeneratedValue Long id;
private String name;
#ManyToOne
private Dojo dojo;
private Workshop() {}
public Workshop(String name, Dojo dojo) {
this.name = name;
this.dojo = dojo;
}
}
So, I have a bidirectionnal 1:n relation between Dojo & Workshop. The #JsonIgnore annotation is here to avoid an infinite loop with the JSON Marshaller.
The repositories are standard
public interface WorkshopRepository extends CrudRepository<Workshop, Long> {}
Now my test : I want to update a workshop. Sounds good, doesn't work.
#Test
public void testUpdateWorkshop() throws Exception {
final String DOJO_NAME="My Dojo";
final String DOJO_LOCATION="Liege";
final String WORKSHOP_NAME="Stuff";
final String HOST_PORT="http://localhost:8080";
//creation of a dojo
final Dojo DOJO = dojoRep.save(new Dojo(DOJO_NAME,DOJO_LOCATION));
//creation of a workshop
Workshop workshop = workshopRep.save(new Workshop(WORKSHOP_NAME,DOJO));
String newValue = "After Test";
System.out.println("before update");
System.out.println(workshop.getName()+" == "+WORKSHOP_NAME);
Long oldID = workshop.getId();
//As you can see I didn't modify the workshop object
HttpEntity<Workshop> entity = new HttpEntity<Workshop>(workshop);
ResponseEntity<String> response = template.exchange(HOST_PORT+"/api/workshops/"+oldID, HttpMethod.PUT, entity, String.class, oldID);
assert response.getStatusCodeValue() == 200;
//re-Get the updated workshop
workshop = workshopRep.findOne(oldID);
System.out.println("after update");
System.out.println(workshop.getName()+" == "+WORKSHOP_NAME);
// as I didn't set the newValue, it must fail and workshop.getName() must stay equal to "Stuff".
Assert.assertEquals("Update does not work",newValue,workshop.getName());
}
I run mvn clean test and
before update
Stuff == Stuff
after update
My Dojo == Stuff
Failed tests:
WorkshopTests.testUpdateWorkshop:218 Update not work expected:<[After Test]> but was:<[My Dojo]>
So basically, I didn't change anything into my object but
Result code is 200.
It changed a property of my object.
The name was modified to take the dojo.name value !
Just ... Why ?
More information :
When I create a new workshop object with a new name (using the newValue ;-) ) and a new Dojo and try to update the existing workshop, the result is still the same. workshop.dojo unchanged and name copied from dojo.name. So basically, my update doesn't work.
I also try with mockMvc instead of TestRestTemplate like this.
mockMvc.perform(put(HOST_PORT+"/api/workshops/"+oldID)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(convertObjectToJsonBytes(workshop))
);
with the function
private byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println("log my face ");
System.out.println(mapper.writeValueAsString(object));
return mapper.writeValueAsBytes(object);
}
And the log seems to rightly parse my object before update...
{"id":1,"name":"Stuff","dojo":{"id":1,"name":"My Dojo","location":"Liege","created":1500799092330}}
but still doesn't work :(
When I run the app (mvn spring-boot:run), a GET on localhost:8080/api/workshops/1 returns
{
"name" : "Stuff",
"_links" : {
"self" : {
"href" : "http://localhost-core:8080/api/workshops/1"
},
"workshop" : {
"href" : "http://localhost-core:8080/api/workshops/1"
},
"dojo" : {
"href" : "http://localhost-core:8080/api/workshops/1/dojo"
}
}
}
If I change the property name of my Dojo class by nameD and I update with a new name and a new Dojo (previously saved into DB), the name is updated but not the dojo.
To summarize my questions are :
Just ... why ?
What is the correct way to update an object like Workshop with a HTTP request ?
What is the correct way to test this update ?
Thanks to all and have a nice day ! :-)
I think it's because you are using bidirectional one-to-many association. In this case you have to provide linking/unlinking of entities by yourself. For example in the collection setter, like this:
#Data
#ToString(exclude = "slaves")
#Entity
public class Master {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "master", cascade = {PERSIST, MERGE})
private List<Slave> slaves;
public void setSlaves(List<Slave> slaves) {
// link new slaves to this master
slaves.forEach(slave -> slave.setMaster(this));
// unlink prev slaves
if (this.slaves != null) this.slaves.forEach(slave -> slave.setMaster(null));
this.slaves = slaves;
}
}
#Data
#Entity
public class Slave {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToOne
private Master master;
}
Then you can store Slave:
POST http://localhost:8080/api/slaves
{
"name": "slave1"
}
// the same for salve2, slave3, slave4
Store Master:
POST http://localhost:8080/api/masters
{
"name": "master1",
"slaves": [
"http://localhost:8080/api/slaves/1",
"http://localhost:8080/api/slaves/2"
]
}
Update Master:
PUT http://localhost:8080/api/masters/1
{
"name": "master1u",
"slaves": [
"http://localhost:8080/api/slaves/3",
"http://localhost:8080/api/slaves/4"
]
}
PUT http://localhost:8080/api/masters/2
{
"name": "master2"
}
Or update Slave:
PUT http://localhost:8080/api/slaves/1
{
"name": "slave1u",
"master": "http://localhost:8080/api/masters/2"
}
PUT http://localhost:8080/api/slaves/2
{
"name": "slave2u",
"master": "http://localhost:8080/api/masters/2"
}
See working example.
Additional info

REST Service - JSON mapping for dynamic parameters

Let us take the following JSON response which I want to return from my REST service,
{
"id" : 123,
"name" : "ABC",
}
For the above JSON response, I can create a POJO class like,
public class Student{
private long id;
private String name;
//getters and setters
}
So, I can write a GET service to return the Student object which will be then transformed as JSON.
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response get(){
Student student = new Student();
student.setId(123);
student.setName("ABC");
return Response.ok(student).build();
}
It works fine. Now I want to introduce optional parameters to my JSON response as follows,
{
"id" : 123,
"name" : "ABC",
"params" : {"param1":"xxx","param2":342}
}
Here the params in the JSON response is an Object type and the attributes of that object are not fixed. It will vary for every request like sometime it can have 3 attributes and sometime it will have none. I don't know how to create my POJO class for this requirement. Can anybody suggest me a way how to do it?
Unless you don't need anything special, you should design it as like:
public class Student{
private long id;
private String name;
//getters and setters
private Map<String, String> parameters = new HashMap<>();
public void add(String key, String value) {
parameters.put(key, value);
}
public void addAll(Map<String, String> map) {
parameters.putAll(map);
}
}
If you need type safety then the design is little bit complicated a consider using something like:
class StudentParameters {
long param1;
String param2;
}
and Student:
public class Student{
private long id;
private String name;
//getters and setters
private StudentParameters studentParameters;
public setStudentParameters(final StudentParameters studentParameters) {
this.studentParameters = studentParameters;
}
}
Do not create complex hierarchies e.g Map<List<List>, List<List>> it will complicate whole structure.

Exercise 115; should I use getters/setters or something else?

I took care of exercise 105. I don't know what to do with 115 though. I've worked a little bit more and progressed a slight bit, but here's the exercise:
A team of biologists is conducting an experiment that involves collecting data on animals founds in a 1 km square area of woodland. As each animal is identified, a record is made of its name, the time of its discovery, and the initials of the scientist who found it. The data are to be recorded on a laptop. Design and implement a system for storing the data, and test your code thoroughly.
[Hint: Think in terms of creating an object for each discovery. What information should each object store, and in what will you store these objects?]
Here is my code:
// put class definitions here
public class Record{
String name;
String initials;
String time;
public Record{
this.name = name;
this.initials = initials;
this.time = time;
}
}
Here's another section for me to test my solution in:
public static void main( String[] args )
{
// test your solution here
}
So I know I'm supposed to make a new Object out of the variables, but do I need to use getters and setters or something? If that was the case it would probably be a lot easier than I'm assuming.
Thx
Getters and setters is a common pattern for getting internal member variables from Objects. They are not mandatory, but it is a good practice.
You need to read some Java and Object Oriented book to understand why we use it that way. You need to understand why encapsulation is encouraged too.
public class Record{
protected String name;
protected String initials;
protected String time;
public Record{
this.name = name;
this.initials = initials;
this.time = time;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
//... etc.
}

Disadvantages of interface objected programming

class Person{
private String name;
private int age;
private String gender;
//......
}
class Student extends Person{
private String id;
private String schoolBelongTo;
//......
}
public void showInfoOf(Person person){
System.out.println(person.getName());
//......
}
When using function "showInfoOf" ,if an object of Peron is used as the param,OK.However,if it is the type Student,I cannot get access to the field id and schoolBelongTo.
So I am confused ,how to ?
Actually, I want to know is this one of its(Interface oriented programming's or Supper class oriented programming's) disadvantages???
Two possible solutions:
You can programatically check the type in showInfoOf (Person), and use a cast to access & print the desired fields; or,
You can define a method on Person which will print/provide the desired info -- and either replace showPersonInfo() with that entirely, or call it into it. This is the more OO way.
Example:
abstract class Person {
private String name;
private int age;
private String gender;
public void printInfo() {
System.out.println( name);
}
}
class Student extends Person{
private String id;
private String schoolBelongTo;
#Override
public void printInfo() {
super.printInfo();
System.out.println( id);
System.out.println( schoolBelongTo);
}
}
public void showInfoOf (Person person){
person.printInfo();
}
In this example, all functionality has moved to Person.printInfo() and there is no real functionality remaining in showInfoOf (Person).
However in the real-world, you'd probably want move versatility in a Person.provideInfo() function -- perhaps returning a LinkedHashMap of fields & values (since unlabelled values on their own, are not great design).
The showInfoOf (Person) function could then handle formatting & printing the values to the specific requirement, leaving the Person.provideInfo() function general & multi-purpose.
in showInfoOf() you would have to check that person is of type Student, then cast it as a Student to get id or schoolBelongsTo

Wicket - DropDownChoice from Enum to Primitive

Im having some problem with DropDownChoice.
I have an Enum with a list of school title like:
public enum StudyTitle {
NONE(null,null),ELEMENTARY("1","Elementary"),COLLEGE("2","College");
private String code;
private String description;
private StudyTitle(String code, String description){
setCode(code);
setDescription(description);
}
[setter and getter]
}
Then I have a Pojo with a String proprerty call "studyTitleCode" where I want to put the code (ex 1 for elementary, 2 for college etc...).
When I create a DropDownChoice Wicket doesn't allow me to have a proprerty Model of type String if the DropDownChoice is of type StudyTitle.
Ex.
[building the listOfStudyTitle as ArrayList of Enums]
DropDownChoice<String> studyLevel = new DropDownChoice<String>("id",new PropertyModel<String>(myPojo,"studyTitleCode"),listOfStudyTitle,new ChoiceRenderer<StudyTitle>("description","code"));
Is there a Method to allow Wicket to link one property of the Enum to the Property of Model?
Thanks
The choice options for an AbstractSingleSelectChoice must match the type of the value model. The only related config option for the DropDownChoice that I'm aware of is the IChoiceRenderer which allows you to set how the enum value is rendered (vs the default call toString()).
One option would be, instead of using the enum instance itself for your choices model, give the enum a String property that can be used:
public enum TestEnum {
ONE ("ONE"),
TWO ("TWO"),
THREE ("THREE");
private String value;
TestEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static List<String> getStringValues()
{
List<String> stringValues = new ArrayList<String>();
for (TestEnum test : values()) {
stringValues.add(test.getValue());
}
return stringValues;
}
}
#Override
protected void onInitialize() {
super.onInitialize();
IModel<String> myStringValueModel = new Model<String>();
add(new DropDownChoice<String>("id", myStringValueModel, TestEnum.getStringValues()));
}