JPA2.0 property access in spring rest data -- some getters not being called - jpa

I am still somewhat of a novice with Spring Boot and Spring Data Rest and hope someone out there with experience in Accessing by Property. Since I cannot change a database which stores types for Letters in an unnormalized fashion (delimited string in a varchar), I thought that I could leverage some logic in properties to overcome this. However I notice that when using property access, some of my getters are never called.
My Model code:
package ...
import ...
#Entity
#Table(name="letters", catalog="clovisdb")
#Access(AccessType.PROPERTY)
public class Letter {
public enum PhoneticType {
VOWEL, SHORT, LONG, COMMON;
public static boolean contains(String s) { ... }
}
public enum PositionType {
ALL, INITIAL, MEDIAL, FINAL;
public static boolean contains(String s) { ... }
}
public enum CaseType {
ALL, LOWER, UPPER;
public static boolean contains(String s) { ... }
}
private int id;
private String name;
private String translit;
private String present;
private List<PhoneticType> phoneticTypes;
private CaseType caseType;
private PositionType positionType;
#Id
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getTranslit() { return translit; }
public void setTranslit(String translit) { this.translit = translit; }
public String getPresent() { return present; }
public void setPresent(String present) { this.present = present; }
public String getTypes() {
StringBuilder sb = new StringBuilder(); //
if (phoneticTypes!=null) for (PhoneticType type : phoneticTypes) sb.append(" ").append(type.name());
if (caseType!=null) sb.append(" ").append(caseType.name());
if (positionType!=null) sb.append(" ").append(positionType.name());
return sb.substring( sb.length()>0?1:0 );
}
public void setTypes(String types) {
List<PhoneticType> phoneticTypes = new ArrayList<PhoneticType>();
CaseType caseType = null;
PositionType positionType = null;
for (String val : Arrays.asList(types.split(" "))) {
String canonicalVal = val.toUpperCase();
if (PhoneticType.contains(canonicalVal)) phoneticTypes.add(PhoneticType.valueOf(canonicalVal));
else if (CaseType.contains(canonicalVal)) caseType = CaseType.valueOf(canonicalVal);
else if (PositionType.contains(canonicalVal)) positionType = PositionType.valueOf(canonicalVal);
}
this.phoneticTypes = phoneticTypes;
this.caseType = (caseType==null)? CaseType.ALL : caseType;
this.positionType = (positionType==null)? PositionType.ALL : positionType;
}
#Override
public String toString() { .... }
}
My Repository/DAO code:
package ...
import ...
#RepositoryRestResource
public interface LetterRepository extends CrudRepository<Letter, Integer> {
List<Letter> findByTypesLike(#Param("types") String types);
}
Hitting this URI: http://mytestserver.com:8080/greekLetters/6
and setting breakpoints on all the getters and setters, I can see that the properties are called in this order:
setId
setName
setPresent
setTranslit
setTypes
(getId not called)
getName
getTranslit
getPresent
(getTypes not called !!)
The json returned for the URI above reflects all the getters called, and there are no errors
{
"name" : "alpha",
"translit" : "`A/",
"present" : "Ἄ",
"_links" : {
"self" : {
"href" : "http://mytestserver.com:8080/letters/6"
}
}
}
But why is my getTypes() not being called and my JSON object missing the “types” attribute? I note that the setter is called, which makes it even stranger to me.
Any help would be appreciated!
Thanks in advance

That's probably because you don't have a field types, so getTypes() isn't a proper getter. Try adding this to your entity
#Transient
private String types;
I don't know how the inner works, but it's possible that the class is first scanned for its fields, and then a getter is called for each field. And since you don't have types field, the getter isn't called. Setter getting called could be a feature but I wouldn't be surprised if it is a bug, because findByTypesLike should translate to find Letters whose types field is like <parameter>, and types is not a field.
Another thing you can try, is to annotate that getter with #JsonInclude. Jackson 2 annotations are supported in Spring versions 3.2+ (also backported to 3.1.2).

Related

Casting an unknown enum value to default enum in spring boot mongo repository actions

I have problem while casting an unknown enum to a default enum in spring boot while using mongo repository.
This is the enum.
public enum EventType implements Serializable
{
WORKDONE("WORKDONE"),
ODRCOM("ODRCOM"),
EXECUTED("EXECUTED"),
REBOOK("REBOOK"),
MANUAL("MANUAL"),
UNKNOWN("UNKNOWN");
private String value;
EventType(final String type) {
this.value = type;
}
#Override
public String toString() {
return value;
}
}
And here is my model class
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Event {
//other properties
#JsonProperty("eventType")
private EventType eventType;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("eventType")
public EventType getEventType() {
return eventType;
}
#JsonProperty("eventType")
public void setEventType(String eventType) {
this.eventType = Optional.ofNullable(EventType.valueOf(eventType)).orElse(EventType.UNKNOWN);
}
//other getters and setters
}
Here is the mongo repository
public interface EventRepository extends MongoRepository<Event, String> {
}
The document stored in the db is of the following stucture
{
...
"eventType" : "REBOOK1",
...
}
Please note that the REBOOK1 is not a valid enum. But the setter should be able to able to cast anything else to type UNKNOWN.
However it gives this exception everytime
No enum constant dk.nuuday.ossieventprocessor.app.model.EventType.REBOOK1
I have tried with adding a custom converter as a configuration but no luck
Any help is greatly appreciated

InvalidDefinitionException: Cannot construct instance of `com.vehicle.datatransferobject.VehicleDTO`

In the REST endpoint I'm building in Spring Boot, I'm trying to pass my vehicleDTO to my controller. But before it reaches my controller, there is an error.
InvalidDefinitionException: Cannot construct instance of
com.vehicle.datatransferobject.VehicleDTO (no Creators, like default
construct, exist): cannot deserialize from Object value (no delegate-
or property-based Creator)
vehicleDTO
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.myvehicle.EngineType;
#JsonInclude(JsonInclude.Include.NON_NULL)
public class VehicleDTO {
#JsonIgnore
private Long id;
#NotNull(message = "vehiclenumber can not be null!")
private String vehiclenumber;
#Min(2)
#NotNull(message = "Seat count can not be less than 2!")
private Integer vehicleseatcount;
#NotNull(message = "Engine Type can not be null!")
private EngineType enginetype;
#Max(5)
private Integer vehiclerating;
private VehicleDTO(Long id, String vehiclenumber, Integer vehicleseatcount, EngineType enginetype,Integer vehiclerating){
this.vehiclenumber=vehiclenumber;
this.vehicleseatcount=vehicleseatcount;
this.enginetype=enginetype;
this.vehiclerating=vehiclerating;
this.id=id;
}
public static VehicleDTOBuilder newBuilder()
{
return new VehicleDTOBuilder();
}
#JsonProperty
public Long getId() {
return id;
}
public String getvehiclenumber() {
return vehiclenumber;
}
public Integer getvehicleseatcount() {
return vehicleseatcount;
}
public EngineType getEnginetype() {
return enginetype;
}
public Integer getvehiclerating() {
return vehiclerating;
}
public static class VehicleDTOBuilder{
private Long id;
private String vehiclenumber;
private Integer vehicleseatcount;
private EngineType enginetype;
private Integer vehiclerating;
public VehicleDTOBuilder setId(Long id) {
this.id = id;
return this;
}
public VehicleDTOBuilder setvehiclenumber(String vehiclenumber) {
this.vehiclenumber = vehiclenumber;
return this;
}
public VehicleDTOBuilder setvehicleseatcount(Integer vehicleseatcount) {
this.vehicleseatcount = vehicleseatcount;
return this;
}
public VehicleDTOBuilder setEnginetype(EngineType enginetype) {
this.enginetype = enginetype;
return this;
}
public VehicleDTOBuilder setvehiclerating(Integer vehiclerating) {
this.vehiclerating = vehiclerating;
return this;
}
public VehicleDTO createVehicleDTO()
{
return new VehicleDTO(id, vehiclenumber, vehicleseatcount, enginetype,vehiclerating);
}
}
}
My DTO has an Enum type called EngineType
public enum EngineType {
ELECTRIC, DIESEL
}
My controller looks like this
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public VehicleDTO addvehicle(#Valid #RequestBody VehicleDTO vehicleDTO)
{
VehicleDO vehicleDO = Mapper.VehicleDO(vehicleDTO);
return Mapper.makeVehicleDTO(Service.addvehicle(vehicleDO));
}
This exception :
InvalidDefinitionException: Cannot construct instance of
com.vehicle.datatransferobject.VehicleDTO (no Creators, like default
construct, exist): cannot deserialize from Object value (no delegate-
or property-based Creator)
means that Jackson didn't find a way to instantiate VehicleDTO that is the default constructor (no arg constructor) or a JsonCreator.
As you use a builder pattern you will configure the VehicleDTO class to make Jackson to instantiate VehicleDTO with the VehicleDTOBuilder such as :
#JsonDeserialize(builder = VehicleDTO.VehicleDTOBuilder.class)
public class VehicleDTO {
...
}
And annotate your builder with JsonPOJOBuilder as :
#JsonPOJOBuilder(buildMethodName = "createVehicleDTO", withPrefix = "set")
public static class VehicleDTOBuilder{
...
}
According to the javadoc, JsonPOJOBuilder is :
used to configure details of a Builder class: instances of which are
used as Builders for deserialized POJO values, instead of POJOs being
instantiated using constructors or factory methods. Note that this
annotation is NOT used to define what is the Builder class for a POJO:
rather, this is determined by JsonDeserialize.builder() property of
JsonDeserialize.
I faced this error when I used Lombok's #Builder and #Data annotations together on a POJO class that is used for connecting to an API (either for consuming or for providing response)
I removed the #Builder annotation and then it is working fine
In my case:
InvalidDefinitionException: Cannot construct instance of com.vehicle.datatransferobject.VehicleDTO (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
for the above exception, I just write Default Constructor which instantiates class and solved the problem.
Default Constructor:
public VehicleDTO() {
super();
// TODO Auto-generated constructor stub
}
If you are using Lombok - the best thing is to add these annotations to your DTO:
#AllArgsConstructor
#RequiredArgsConstructor
#Data
#Builder (optional)
In addition to davidxxx`s answer. I used Lombok. And in my case it looked like this:
#Data
#JsonDeserialize(builder = SomeClass.SomeClassBuilder.class)
#Builder(builderClassName = "SomeClassBuilder")
public class SomeClass {
// ...
#JsonPOJOBuilder(withPrefix = "")
public static class SomeClassBuilder {
}
}

How to implement LeafValueEditor<Address>

I am trying to understand how to correctly implement a LeafValueEditor for a non immutable object. Which of the two way is correct, or should something else be used?
public class Address {
public String line1;
public String city;
public String zip;
}
Option 1:
public class AddressEditor implements LeafValueEditor<Address>
{
private String line1;
private String city;
private String zip;
private Address address;
public void setValue(Address value)
{
this.line1 = value.line1;
this.city = value.city;
this.zip = value.zip;
this.address = value;
}
public Address getValue()
{
this.address.line1 = this.line1;
this.address.city = this.city;
this.address.zip = this.zip;
return this.address;
}
}
Option 2:
public class AddressEditor implements LeafValueEditor<Address>
{
private String line1;
private String city;
private String zip;
public void setValue(Address value)
{
this.line1 = value.line1;
this.city = value.city;
this.zip = value.zip;
}
public Address getValue()
{
Address a = new Address();
this.a.line1 = this.line1;
this.a.city = this.city;
this.a.zip = this.zip;
return a;
}
}
Probably neither, though both technically could work.
A LeafValueEditor is an Editor for leaf values - that is, values that don't generally contain other values. Usually a text or date or number field that would be visible on the page is the leaf editor, and those leaf nodes are contained in a normal Editor.
In this case, it could look something like this:
public class AddressEditor extends Composite implements Editor<Address> {
// not private, fields must be visible for the driver to manipulate them
// automatically, could be package-protected, protected, or public
protected TextBox line1;//automatically maps to getLine1()/setLine1(String)
protected TextBox city;
protected TextBox zip;
public AddressEditor() {
//TODO build the fields, attach them to some parent, and
// initWidget with them
}
}
See http://www.gwtproject.org/doc/latest/DevGuideUiEditors.html#Editor_contract for more details on how this all comes together automatically with just that little wiring.

JPA, How to find an object that has composite id?

Based on second approach answered here I designed my JPA class.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SearchKeywordJPA that = (SearchKeywordJPA) o;
if (date != that.date) return false;
if (!keyword.equals(that.keyword)) return false;
if (!userUUID.equals(that.userUUID)) return false;
return true;
}
#Override
public int hashCode() {
int result = keyword.hashCode();
result = 31 * result + (int) (date ^ (date >>> 32));
result = 31 * result + userUUID.hashCode();
return result;
}
#Override
public String toString() {
return "SearchKeywordJPA{" +
"keyword='" + keyword + '\'' +
", date=" + date +
", userUUID='" + userUUID + '\'' +
'}';
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public long getDate() {
return date;
}
public void setDate(long date) {
this.date = date;
}
public String getUserUUID() {
return userUUID;
}
public void setUserUUID(String userUUID) {
this.userUUID = userUUID;
}
#Override
public int compareTo(SearchKeywordJPA searchRecord) {
long comparedDate = searchRecord.date;
if (this.date > comparedDate) {
return 1;
} else if (this.date == comparedDate) {
return 0;
} else {
return -1;
}
}
/**********************
* Key class
**********************/
public class SearchKeyId {
private int id;
private int version;
}
}
In my servlet I want to check datastore and store my object if is not exist.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
for(SearchKeywordJPA item: applicationList) {
if(!isRecorded(item))
storeRecord(item);
}
}
private boolean isRecorded(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
SearchKeywordJPA item = em.find(SearchKeywordJPA.class, record);
return item != null;
}
private void storeRecord(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
em.persist(record);
}
However when I run, application crashes and log says
javax.persistence.PersistenceException: org.datanucleus.store.appengine.FatalNucleusUserException: Received a request to find an object of type com.twitterjaya.model.SearchKeywordJPA identified by SearchKeywordJPA{keyword='airasia', date=1335680686149, userUUID='FFFF0000'}. This is not a valid representation of a primary key for an instance of com.twitterjaya.model.SearchKeywordJPA.
What is the reason? any suggestion would be appreciated. Thanks
You pass an instance of the IdClass into em.find ... i.e SearchKeyId. Obviously if you really have an IdClass that has no equals/hashCode/toString/constructor then you will likely get many problems. Those problems will only be increased by using an ancient plugin for GAE/Datastore.
If your Key is
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
you are doing it wrong.
IdClass does not need any annotation of #IdClass just the #Id
annotation.
Key can not be an entity.
Need to implements Serializable , comparable is not needed
Need to override equals and hascode and have no arg constructor
Class key need to be as follows.
public class SearchKeyId implements Serializable {
private String keyword;
private long date;
And your entity I assume something like this.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeyId.class)
public class SearchKeywordJPA {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
Just consider that find method will use the SearchKey.class to find
the entities.
Fields that are in the IdClass need to have #Id annotation in the entity.
Key can not be an entity on its own.
Comparable is not really needed as all the comparison are placed in the IdClass

BLOB as parameter in procedure call in mybatis

This is the call in the ProductServices.xml
<update id="resetPassword" parameterType="batchReport">
{ call user_account_mng.enc_reset_password(
#{user_Id,jdbcType=VARCHAR,mode=IN},
#{encrypted_password,jdbcType=VARCHAR,mode=IN},
#{usr_id, dbcType=VARCHAR,mode=IN},
#{salt,jdbcType=VARCHAR,mode=IN},
#{ret_code,jdbcType=CHAR,mode=OUT},
#{pgp_encrypted_password,jdbcType=BLOB,mode=IN}
)}
Now BatchReport is a POJO:
(i have declared an alias for it as batchReport)
public class BatchReport
{
private String user_Id;
private String encrypted_password;
private String usr_id;
private String salt;
private String ret_code;
private byte[] pgp_encrypted_password;
public String getUser_Id() {
return user_Id;
}
public void setUser_Id(String user_Id) {
this.user_Id = user_Id;
}
public String getEncrypted_password() {
return encrypted_password;
}
public void setEncrypted_password(String encrypted_password) {
this.encrypted_password = encrypted_password;
}
public String getUsr_id() {
return usr_id;
}
public void setUsr_id(String usr_id) {
this.usr_id = usr_id;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getRet_code() {
return ret_code;
}
public void setRet_code(String ret_code) {
this.ret_code = ret_code;
}
public byte[] getPgp_encrypted_password() {
return pgp_encrypted_password;
}
public void setPgp_encrypted_password(byte[] pgp_encrypted_password) {
this.pgp_encrypted_password = pgp_encrypted_password;
}
}
My main class is like this :
<BatchReport batchReport = new BatchReport();
byte[] byteArray =new byte[]{1,2,3};
batchReport.setUser_Id("CHI");
batchReport.setEncrypted_password("97D6B45");
batchReport.setSalt("71L");
batchReport.setPgp_encrypted_password(byteArray);
String returnCode = productServiceObj.resetPassword(batchReport);
i am getting following error:
Error setting null parameter. Most JDBC drivers require that the JdbcType must be specified for all nullable parameters. Cause: java.sql.SQLException: Invalid column type
The error may involve com.example.services.ProductServices.resetPassword-Inline
ProductServices is a class in which the method resetPassword is declared.
Please help me with this BLOB issue.
What should be the jdbcType in the called procedure.
what value should be passed in this pgp_encrypted_password.
Okay I found the solution to the problem now the jdbcType in the query in .xml file remains the same i.e BLOB.
Next the type which gets set for passing in the values is byte[].
So everything remains same as i have covered up .
Error actually existed as the in .xml file returns an integer indicating the number of rows changed in query and I have given the function return type as String so here goes the solution for the problem it should be of type Object.