It is storing an integer in the database not the string as I requested.
Here is the class that contains the enum.
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class Document extends BaseModel {
private String title = new String();
private String description = new String();
**#Enumerated(EnumType.STRING)
private DocumentType documentType;**
#Embedded
private DocumentImage documentImage;
// if document should be displayed or published on the web site.
private Boolean published = new Boolean(false);
public Document(DocumentType docType) {
super();
documentType = docType;
setDocumentImage(new DocumentImage());
}
}
and here is the enum class:
public enum DocumentType {
policy,procedure,webbookmark,newsrelease,collectionLetter,whitepaper,busform,
newsarticle ;
}
I know this should work. Any ideas?
One possible reason is that your #Enumerated annotation doesn't take effect because annotations in BaseModel are placed on properties rather than on fields. Placement of annotations on fields or properties should be consistent across inheritance hierarchy.
Related
I'm currently using Quarkus combined with Blaze Persistence for my microservice. I have the following entity model:
#Entity
public class Content extends BaseEntity {
private boolean deleted;
private boolean published;
}
#Entity
public class WebContent extends Content {
private String webpage;
}
I've mapped the entities to the following EntityViews:
#EntityView(Content.class)
#EntityViewInheritance
#CreatableEntityView
#UpdatableEntityView
public interface ContentUpdateView {
#IdMapping
Long getId();
boolean isPublished();
void setPublished(boolean published);
}
#EntityView(WebContent.class)
#CreatableEntityView
#UpdatableEntityView
public interface WebContentUpdateView extends ContentUpdateView {
String getWebpage();
void setWebpage(String webpage);
}
I have the following method in my ContentsResource:
#POST
public ContentUpdateView save(ContentUpdateView content) {
return contentsService.save(content);
}
When I invoke the post operation I only get the base ContentUpdateView and not the WebContentUpdateView. Is there any configuration to do? (with Jackson I use #JsonTypeInfo and #JsonSubType annotation on the entities to accomplish this).
Thanks
euks
I got it working using Jackson annotation. Here's the base class:
#EntityView(Content.class)
#EntityViewInheritance
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = DescriptiveContentView.class, name = "descriptive"),
#JsonSubTypes.Type(value = MediaContentView.class, name = "media"),
#JsonSubTypes.Type(value = WebContentView.class, name = "web")
})
#JsonTypeName("content")
public abstract class ContentView {
#IdMapping
public abstract Long getId();
public abstract boolean isPublished();
}
And here's a subclass:
#EntityView(DescriptiveContent.class)
#JsonTypeName("descriptive")
public abstract class DescriptiveContentView extends ContentView {
public abstract Set<LocalizedParagraphView> getLocalizedParagraphs();
}
I'm using abstract classes for other purposes, but it also works with interfaces.
i'm facing this issue while using Spring JPA and trying to retrieve a List of objects.
This is the class i'm trying to retrieve
#Entity
#Table(name="OBJECTSTERMIC")
public class TermicObject {
#Id
#Column(name="TERMICID")
private long termicId;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="OBJECTID",columnDefinition="INTEGER")
private Object object;
#Column(name="CONTECA_RIF")
private int contecaRif;
#Column(name="CONTECA_VAL")
private int contecaVal;
#Column(name="TYPE")
private String type;
//getters and setters
The Object class has the primary key on MySQL stored as an Integer, indeed this is Object
#Entity
public class Object {
#Column(name="OBJECTID")
#Id
#JsonProperty("OBJECTID")
private int objectId;
....
So, nowhere is set a Long...
Now, i simply call in a service class
#Override
public List<TermicObject> findAll() {
return repository.findAll();
}
and got this exception
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TypeMismatchException: Provided id of the wrong type for class it.besmart.db_eipo.persistence.model.Object. Expected: class java.lang.Integer, got class java.lang.Long; nested exception is java.lang.IllegalArgumentException: org.hibernate.TypeMismatchException: Provided id of the wrong type for class it.besmart.db_eipo.persistence.model.Object. Expected: class java.lang.Integer, got class java.lang.Long
Where is set that Object Id should be Long?
Have a look at definition of your repository. Does it have right generic type? do you have Integer as second parameter? IMHO this can be root cause. See proposed correct version:
#RepositoryRestResource
public interface TermicObjectRepository extends JpaRepository<TermicObject, Integer> {
public Optional<TermicObject> findById(Integer id);
public List<TermicObject> findAll()
}
As per #Lubo's answer, in my case I was having compatibility issues between String and Long types and as my model required a Long autogenerated id I had to change the repository from
public interface ProductRepository extends JpaRepository<Product, String> {
}
to
public interface ProductRepository extends JpaRepository<Product, Long> {
}
And my controller from
#RequestMapping(path = "/products/delete/{id}", method = RequestMethod.DELETE)
public void deleteProduct(#PathVariable(name = "id") String id) {
productRepository.deleteById(id);
}
to
#RequestMapping(path = "/products/delete/{id}", method = RequestMethod.DELETE)
public void deleteProduct(#PathVariable(name = "id") Long id) {
productRepository.deleteById(id);
}
You have to define your id as a Long datatype.
#Id
#Column(name="TERMICID")
private Long termicId;
also make a change in your repository interface:
public interface ProductRepository extends JpaRepository<Product, Long> {
}
Got this because
public class MyEntity {
#Id()
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private int id; // <-------- int
...
public long getId() { return id; } // <-------- long
}
Not completely sure, but I think this mapping
#Id
#Column(name="TERMICID")
private long termicId;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="OBJECTID",columnDefinition="INTEGER")
private Object object;
Makes the id of the Object match the value of termicId which is a long.
use
Long.valueOf(intValue)
to cast int to Long type because you define type Long to #Id
With the following classes, which make up the JSON message structure I transfer to the client side after a JAX-RS call (CXF), I receive
org.apache.cxf.jaxrs.client.ClientWebApplicationException: .Problem with reading the response message, class : class bg.vivacom.sel.dto.SELResponse, ContentType : application/json.
...
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of bg.vivacom.sel.dto.SELDTO, problem: abstract types can only be instantiated with additional type information
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#16edbe39; line: 1, column: 157] (through reference chain: bg.vivacom.sel.dto.SELResponse["dto"])
The returned object contains property dto which is an interface
#XmlRootElement(name = "SELResponse")
public class SELResponse implements Serializable{
#XmlElement(name = "corelationID")
private String corelationID;
#XmlElement(name = "responseTimeStamp")
private String responseTimeStamp;
#XmlElement(name = "respStatusCode")
private String respStatusCode;
#XmlElement(name = "respStatusMessage")
private String respStatusMessage;
#XmlElement(name = "processStatus")
private ProcessStatus processStatus;
#XmlElement(name = "dto")
private SELDTO dto;
The interface
public interface SELDTO extends Serializable {
is a way to include a number of different DTOs in my answer depending on the request, such as
#XmlRootElement(name = "customerProfile")
public class CustomerProfileDTO implements SELDTO {
#XmlElement(name = "customerCode")
private String customerCode;
...
and
#XmlRootElement(name = "sms")
public class SmsDTO implements SELDTO {
#XmlElement(name = "From")
private String from;
...
Any ideas how else I have to annotate the classes so that the response can be correctly set to the specific object type. I understand that it requires additional information as at the time it re-creates the dto object it doesn't know its type so I have tried to annotate the interface as follows:
#XmlSeeAlso({CustomerProfileDTO.class, SmsDTO .class})
public interface SELDTO extends Serializable {
but I still get the issue. Any thoughts would be appreciated it.
I would recommend using Jackson annotation '#JsonTypeInfo' for this case.
But if you must use JAXB annotations, use #XmlElements or #XmlElementRefs similar to how you'd use them with JAXB/XML:
#XmlElements({
#XmlElement(type=SubClass1.class, name="sub1"),
#XmlElement(type=SubClass2.class, name="sub2")
})
note that you must include specific mapping to possible subtypes here.
I'm facing problem with Jackson's ObjectMapper using JAXB annotations. To be concrete, I'm having collection with interface generic information and although I can deserialize input from XML, it is not possible with Jackson (using JAXB introspector). Maybe I'm just missing some configuration property or JAXB annotation? The problem is that "abstract types can only be instantiated with additional type information", I thought #XmlElementRef (or #XmlElement) with type information will handle this problem, but obviosly it does not.
Please note, that I want to stay only with JAXB annotations if possible.
E.g. using #JsonTypeInfo or #JsonDeserialize would be the last thing to do.
IEntry.java:
#XmlSeeAlso(Entry.class)
public interface IEntry {
String getValue();
}
Entry.java:
#XmlRootElement(name = "entry")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Aggregator.java:
#XmlRootElement(name = "aggregator")
public class Aggregator {
#XmlElementRef(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
Test method:
#Test
public void testSerialization() throws Exception {
ObjectMapper om = new ObjectMapper();
AnnotationIntrospector intr = new JaxbAnnotationIntrospector();
om.getDeserializationConfig().withAnnotationIntrospector(intr);
String json = "{\"entries\":[{\"value\":\"X\"},{\"value\":\"Y\"},{\"value\":\"Z\"}]}\";\n}";
Aggregator agr = om.readValue(json, Aggregator.class);
}
Thanks for all response
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
I am not sure if Jackson supports this use case or not, but you appear to be using #XmlElementRef incorrectly. When you use #XmlElementRef the root element name associated with the class is used to determine the instance to be instantiated. If your example the node entries does not match the #XmlRootElement(name="entry") annotation.
You could try one of the following options (they all work with MOXy's JSON binding, see: http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html):
OPTION 1 - Change #XMLRootElement on Entry
#XmlRootElement(name = "entries")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
OPTION #2 - Change the JSON Document
{"entry":[{"value":"X"},{"value":"Y"},{"value":"Z"}]}}
OPTION #3 - Use #XMLElement instead of #XMLElementRef
If you use the #XmlElement annotation you can specify on the field/property what the node name should be instead of relying on the #XmlRootElement annotation. Also if you annotate the fields you should specify #XmlAccessorType(XmlAccessType.FIELD) at the type level.
import java.util.Set;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "aggregator")
#XmlAccessorType(XmlAccessType.FIELD)
public class Aggregator {
#XmlElement(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html
import org.codehaus.jackson.map.annotate.JsonDeserialize;
#JsonDeserialize(as = Entry.class)
public interface IEntry {
String getValue();
}
I use DataContractJsonSerializer to deserialize json data in Silverlight 4.
Json data key names do not match my class property names; so I guess I have to use
DataMemberAttribute. So I did the following:
[DataContract]
public class Person : Model
{
[DataMember(Name = "id")]
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
[DataMember(Name = "name")]
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Now deserialization fails because I didn't apply DataContractAttribute to Person's base class Model. Is it a strict requirement? Also, after I applied DataContractAttribute to Model, deserialization fails again, because I applied DataMember attributes to private fields, not to the public properties. Why can't I apply them to private members (the documentation seems to say otherwise).
NOTE: server-side code is not ASP.NET; so WCF isn't used.
In order to get the private members to serialize over WCF correctly, we had to change them all to protected internal instead of private. Maybe the same applies for DataContractJsonSerializer?