I have a table in the database with fields like below which holds my form input's attributes:
#Column(name = "propertyname")
private String propertyname;
#Column(name = "propertyvalue")
private String propertyvalue;
#Column(name = "propertytype")
private String propertytype;
#Column(name = "propertyrequired")
private String propertyrequired;
#Column(name = "propertysize")
private String propertysize;
#Column(name = "propertymin")
private String propertymin;
Now I am getting these as bean fields in Thymeleaf template. But keep getting template parsing error (on the same line as below). I am trying to achieve below:
<input th:field="${modelAttribute.beanPropertyList[__${rowStat.index}__].propertyname}" th:errorclass="is-invalid" th:required="${modelAttribute.beanPropertyList[__${rowStat.index}__].propertyrequired}" th:min="${modelAttribute.beanPropertyList[__${rowStat.index}__].propertymin}" th:max="${modelAttribute.beanPropertyList[__${rowStat.index}__].propertymax}"/>
What am I doing wrong? or what is the correct approach for this?
th:field doesn't work like that. It requires a th:object="${foo} as a form backing bean. Then th:field="*{bar}" will set the input's name="bar" and try to reflect on the model object called foo and get its field call bar to use the input's value
Try th:name and th:value separately. That also seems more plausible given your model.
Related
When sending an Object to a JSP page the persistent and transient fields are left out. I can see on the Java side these variables are filled in with data, but once it gets to the JSP page some of the values are missing, specifically every field that is not mapped to a column.
Group Entity
#Entity
#Table(name="groups")
#XmlRootElement
public class Groups {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="groupsSeqGen")
//TODO: I dont think H2 is having the sequences auto generated. Need to add these manually.
#SequenceGenerator(name="groupsSeqGen",sequenceName="groups_sequence", initialValue = 10, allocationSize = 100)
private Long id;
#Column(name="name")
private String name;
#Column(name="create_date")
private Date createDate;
#Column(name="owner_user")
private String ownerUser;
#Column(name="is_public")
private Boolean isPublic;
#Column(name="description")
private String description;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "ownerGroup")
private List<Books> books;
}
Request Mapping
#RequestMapping("/Mygroups")
public ModelAndView getMyGroup() {
ModelAndView mav=new ModelAndView();
mav.addObject("groups", appservice.findMyGroups()); //This returns the groups!
mav.setViewName("myGroups");
return mav;
}
My JSP page can read the groups. Just for an idea here is the console output when I print the object.
Groups [id=1, name=Club 1, createDate=2019-08-01 00:00:00.0, description=Club 1 desc, isPublic=true, ownerUser=user1]
What i've tried.
Adding #transient and #XMLTransient tags.
Joining columns differently.
Changing the Fetch type (This doesnt matter im just changing random things at this point)
The other Odd Part is when I write to the object with a form I can set these fields fine! Maybe its because Javascript is just setting the fields regardless of if it matches and when Java reads it in when it does match it works correctly?
I'm an idiot....
It didn't show up in my JavaScript console output because I skipped adding it to the ToString in my Groups class. Once I added it there. I realized I was just referencing it incorrectly in JavaScript.I think I was referencing it as "books" instead of "group.books" and didn't realize because I was printing the object and it wasn't there.
In other words, the common Jackson markup is not enough for serializing the same entity for using as the REST request response to the Angular frontend and to pass the object to Elasticsearch via the Jest client. Say, I have an image in the Entity as a byte array, and I'd like it to be stored to DB and be passed to the frontend, but don't like it being indexed by Elasticsearch to reduce the costs or quotas.
Now I have to use Jackson's JsonView to markup the fields to use for the Spring Data Elasticsearch's ObjectMapper:
#Entity
#Table(name = "good")
#org.springframework.data.elasticsearch.annotations.Document(indexName = "good", shards = 1, replicas = 0, refreshInterval = "-1")
public class Good implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#org.springframework.data.elasticsearch.annotations.Field(type = FieldType.Keyword)
#JsonView(Views.Elasticsearch.class)
private Long id;
#NotNull
#Column(name = "short_name", nullable = false)
#JsonView(Views.Elasticsearch.class)
#Field(store = true, index = true, type=FieldType.Text)
private String shortName;
#NotNull
#Column(name = "description", nullable = false)
#JsonView(Views.Elasticsearch.class)
#Field(store = true, index = true, type=FieldType.Text)
private String description;
#Lob
#Column(name = "image", nullable = false)
#JsonView(Views.Rest.class)
private byte[] image;
#Column(name = "image_content_type", nullable = false)
#JsonView(Views.Rest.class)
private String imageContentType;
#NotNull
#Column(name = "price", nullable = false)
#JsonView(Views.Elasticsearch.class)
#Field(store = true, index = true, type=FieldType.Integer)
private Integer price;
...
I have a clss for Views:
public class Views {
public static class Rest{}
public static class Elasticsearch{}
}
And the ObjectMapper set up in the corresponding Bean:
#Override
public String mapToString(Object object) throws IOException {
log.trace("Object to convert to JSON : {}",object);
log.trace("Converting to json for elasticsearch >>> {}",objectMapper.writer().withView(Views.Elasticsearch.class).writeValueAsString(object));
//log.trace("Converting to json for elasticsearch >>> {}",objectMapper.writeValueAsString(object));
return objectMapper.writerWithView(Views.Elasticsearch.class).writeValueAsString(object);
//return objectMapper.writeValueAsString(object);
}
So, I have to markup all the fields except the ignored to Elasticsearch with #JsonView(Views.Elasticsearch.class) and this is the error prone. Also, this field still requires #Field usage if I like to pass some parameters there like store or value type. When I have #JsonView(Views.Elasticsearch.class), but don't have #Field on some, the fields are created in the index on a fly, that allows them to search, but not in desired way.
The latest is the reason why if I just leave #Field there and don't place it over fields I don't want to index into Elasticsearch, the initial index indeed ignores them, but later requests pass the undesired field when the entity is serialized exactly the same way as it is done for the REST. And the index property is created on a fly, making resources being spent for the large binary object indexing. So it looks like #Field is used for the initial index creation on the startup, but are not configured to be used with ObjectMapper of the Spring Data Elasticsearch.
So, I'd like to make this ObjectMapper take only fields with #Field above them into account, i.e serialize the fields marked with #Field only and use no #JsonView staff. How can I configure it?
These are known problems when using the Jackson Object Mapper in Spring Data Elasticsearch (which als is the default) and this is one of the reasons, why in Spring Data Elasticsearch as of version 3.2 (which currently is in RC2 and will be available as 3.2.0.GA in mid-september), there is a different mapper available, the ElasticsearchEntityMapper.
This mapper still has to be setup explicitly, the reference documentation of 3.2.0.RC2 shows how to do this. Using this mapper the Jackson annotations do not influence the data stored in and read from Elasticsearch. And you can use the org.springframework.data.annotation.Transient annotation on a field to not have it stored in Elasticsearch.
The #Field annotation is used to setup the initial Elasticsearch mapping, properties not having this annotation are automatically mapped by Elasticsearch when they are inserted.
I have this API that uses #CreationTimestamp to auto-generate the date when any information is added to the database, and it works. The problem is, when i use the PUT method in a specific information of the database, the generated Date disappears.
This is how things are structured:
...
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CreationDate", length = 29)
#CreationTimestamp
private Date CreationDate;
...
This is how my PUT method is:
#PutMapping("/SomeURL/{id}")
#ResponseBody
public User editUser(#RequestBody #Valid User T, #PathVariable Integer id) {
return userRepository.save(T);
}
When I use this, the generated Date from the #CreationTimestamp disappears, and that field becomes blank (null).
Why is this happening?
OBS: I dont think is necessary, but this is my POST method:
#PostMapping("/someURL")
public User addUser(#RequestBody #Valid User T) {
return userRepository.save(T);
}
The creation date will be updated when calling save method within your editUser method. The entity possibly does not contain this value, therefore null will be set as updated value. (you can try to debug this to check)
Change the column annotation as followed:
#Column(name = "CreationDate", length = 29, nullable = false, updatable = false)
This should prevent the once created date to be updated.
HTH
I have an Springboot application integrated with couchbase 6.0.
I have read that if a key is annotated with #Id then it will saved as an document id and will not be a part of the json.
Then i have used #Id and #Field together on the key but still that field is not appearing on json document.
Can somebody help me out on the following:
1: How to make same key as document id and keep that a part of json also.
2: If a field is declared with #Id, it is created as document id and does
not appear on document, but when i do a get request same key appear on
the response.
I have also tried, click here https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#couchbase.autokeygeneration.usingattributes
My entity is:
#Id #GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES,delimiter="::")
private String id;
#IdAttribute
private String userid;
#Field
private String fname;
#Field
private String lname;
Post method body:
{
"id": "888",
"userid":"user1",
"fname": "xyz",
"lname": "abc"
}
In Couchbase server, it is saving this document as
It is creating document id as 888 only, it is supposed to generate documnet id as 888::user1
I have tested this many times but the result is same.
Looks like you're looking for Key generation using attributes:
It is a common practice to generate keys using a combination of the
document attributes. Key generation using attributes concatenates all
the attribute values annotated with IdAttribute, based on the ordering
provided similar to prefixes and suffixes.
So, for example you can do:
#Document
public class User {
#Id #GeneratedValue(strategy = USE_ATTRIBUTES)
private String id;
#IdAttribute
private String userid;
...
}
According to the example above you will get as a result a Document with the id = userId and also the field userId in the json.
UPDATE AS PER QUESTION EDIT
You may have to include a new filed in your entity, let's say postId and change the naming of the id field in your post method body by postId, something like:
Entity:
#Id #GeneratedValue(strategy = GenerationStrategy.USE_ATTRIBUTES,delimiter="::")
private String id;
#IdAttribute(order = 0)
private String postId;
#IdAttribute(order = 1)
private String userid;
#Field
private String fname;
#Field
private String lname;
Post method body:
{
"postId": "888",
"userid":"user1",
"fname": "xyz",
"lname": "abc"
}
I am not so into Spring Data JPA and I have the following doubt about how to implement a simple query.
I have this AccomodationMedia entity class mapping the accomodation_media on my database:
#Entity
#Table(name = "accomodation_media")
public class AccomodationMedia {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "id_accomodation")
private Long idAccomodation;
#Column(name = "is_master")
private boolean isMaster;
#Lob
#Column(name = "media")
private byte[] media;
private String description;
private Date time_stamp;
public AccomodationMedia() {
}
...............................................................
...............................................................
...............................................................
// GETTER AND SETTER METHODS
...............................................................
...............................................................
...............................................................
}
The instance of this class represents the photo associated to an accomodation (an hotel)
So as you can see in the prvious code snippet I have this field :
#Column(name = "id_accomodation")
private Long idAccomodation;
that contains the id of an accomodation (the id of an hotel on my database).
I also have this boolean field that specify if an image is the master image or not:
#Column(name = "is_master")
private boolean isMaster;
So, at this time, in my repository class I have this method that should return all the images associated to a specific hotel:
#Repository
public interface AccomodationMediaDAO extends JpaRepository<AccomodationMedia, Long> {
List<AccomodationMedia> findByIdAccomodation(Long accomodationId);
}
I want to modify this method passing also the boolean parameter that specify if have to be returned also the master image or only the images that are not master.
So I tryied doing in this way:
List<AccomodationMedia> findByIdAccomodationAndIsMaster(Long accomodationId, boolean isMaster);
but this is not correct because setting to true the isMaster parameter it will return only the master image (because it is first selecting all the Accomodation having a specific accomodation ID and then the one that have the isMaster field setted as true).
So, how can I correctly create this query that use the isMaster boolean parameter to include or exclude the AccomodationMedia instance that represent my master image?
I know that I can use also native SQL or HQL to do it but I prefer do it using the "query creation from method names"
I don't have how to test this, but essentially your final query should be:
id_accomodation = ?1 AND (is_master = ?2 OR is_master = false)
So I would try the following method signature:
findByIdAccomodationAndIsMasterOrIsMasterFalse(Long accomodationId, boolean isMaster);
I would go with two methods one for isMaster true, while second for false value like this:
List<AccomodationMedia> findByIdAccomodationAndIsMasterFalse(Long accomodationId);
List<AccomodationMedia> findByIdAccomodationAndIsMasterTrue(Long accomodationId);
Change your acommodation id as accomodationId instead of idAccomodation. When you write findByIdAccomodationAndIsMaster spring confusing findByIdAccomodationAndIsMaster
Try this this convention
#Column(name = "accomodation_id")
private Long accomodationId;