I have an abstract class:
#MappedSuperclass
public abstract class BaseEntity<K>
{
#Temporal(value = TemporalType.TIMESTAMP)
private Date cadastrado;
#Temporal(value = TemporalType.TIMESTAMP)
private Date modificado;
#Column(length = 30)
private String ip;
private String autorModificacao;
public abstract K getId();
public abstract void setId(K id);
...
and a derived class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Pessoa extends BaseEntity<Integer> implements Serializable {
#Id
#ColumnGridPF
#GeneratedValue(strategy = GenerationType.AUTO, generator = "pessoa")
private Integer id;
....
#Override
Integer getId() {
return id;
}
#Override
public void setId(Integer id) {
this.id = id;
}
....
when my application try to unmarshall the object, I get an error
**
SEVERE: The RuntimeException could not be mapped to a response, re-throwing to the HTTP container
java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to java.lang.Integer
at br.com.sigmaonline.entity.cadastro.pessoa.Pessoa.setId(Pessoa.java:46)
at br.com.sigmaonline.entity.common.generic.BaseEntity$JaxbAccessorM_getId_setId_java_lang_Object.set(MethodAccessor_Ref.java:60)
**
Can Any one help me?
By default when your JAXB (JSR-222) implementation is creating metadata for Pessoa it is also going to create metadata for the super class BaseEntity. Since JAXB by default considers properties as mapped it is going to consider that it has a property called id of type Object. When JAXB doesn't know the type of the property it will convert it to a DOM Element. This is resulting in the ClassCastException.
Solution
The solution really depends upon whether or not you want BaseEntity considered part of the inheritance hierachy (see: http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html). But what I would recommend is either leveraging #XmlTransient or #XmlAccessorType(XmlAccessType.NONE) on BaseType to remove problematic properties:
http://blog.bdoughan.com/2012/04/jaxb-and-unmapped-properties.html
Related
I have this generic entity:
#MappedSuperclass
abstract class Position<T> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Enumerated(EnumType.STRING)
private T name;
}
But there's a case where the generic type is a String:
#Entity
class ChildPosition0 extends Position<String> {
}
And, JPA will complaint that String is not an enum in this case, but I need to annotate this name field if it's an enum, if not, the database will mark it as int type, and that's not ideal. How do I solve this? How to annotate the field conditionally?
My workaround:
Use Position as a parent class, and adding those field in child class individually, even though they share the same field:
#MappedSuperclass
abstract class Position {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
And extends it from child entity like this:
Child1:
#Entity
public class ChildPosition1 extends Position {
#Enumerated(EnumType.STRING)
private Priority name; // <- Priority is enum type
}
Child2:
#Entity
public class ChildPosition2 extends Position {
private String name;
}
This is too ugly IMO. And Java does not allow class field override from child class. So, back to the question: how to annotate generic field conditionally?
I am new with JPA, so maybe someone can explain me how to correctly annotate abstract classes using JPA?
I have an abstract class with generated id field:
public abstract class AbstractClass implements Serializable {
private static final long serialVersionUID = 1L;
private long id;
#Id
#GeneratedValue
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
and an abstract class with name which extends AbstractClass:
public abstract class AbstractNameClass extends AbstractClass {
private String shortName;
#Column(name = "shortName", nullable = false)
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
}
I have two types of classes, one extends AbstractClass and other classes extends AbstractNameClass:
#Entity
public class Model extends AbstractNameClass {
// this class should inherit id (from AbstractClass) and shortName (from AbstractNameClass)
}
and
#Entity
public class Vehicle extends AbstractClass {
// this class should inherit only id
}
If I add #MappedSuperclass annotation on AbstractClass, then I can create and save objects which are extending AbstractClass, but how to annotate AbstractNameClass? I tried to add #Entity annotation, but I got "No identifier specified for entity" error, also I tried to add #MappedSuperclass annotation and also got the same error.
So my question would be - how to correctly annotate abstract classes using JPA, without creating AbstractClass and AbstractNameClass tables (in my db I want to have only Model and Vehicle tables)?
I'm writing an application using Hibernate and I was wondering how should I map an entity defined as:
#Entity
public Class MyEntity implements Serializable {
#Id
private Long id;
#Column
private Class<?> clazz;
// getters/setters...
}
Is it possible to map this to a DB? Should I add an extra annotation to clazz?
Note that this is a curiosity, I'm not in a hurry this time, so please do not try to supply alternate solutions. Thanks in advance for your answers.
There is no JPA mapping for a Class object out of the box, but to satisfy most (if not all) use cases, you could do something like this
#Entity
public Class MyEntity implements Serializable {
#Id
private Long id;
#Column
private String clazz;
public Class getClazz () {
return this.clazz != null ? Class.forName(this.clazz) : null;
}
public Class setClazz (Class clazz) {
this.clazz = clazz.getName();
}
}
In this case, you are simply storing the name of the class as a String, and then when you retrieve it from the database, the getter will convert it into a proper class object.
I follow this post to resolve my initial problem:
Specifying distinct sequence per table in Hibernate on subclasses
But now I get an exception:
Invocation of init method failed; nested exception is
java.lang.IllegalArgumentException: Duplicate generator name idgen
Below my class, subclass and pom.xml:
EntityId (abstract class)
#MappedSuperclass
public abstract class EntityId<T extends Serializable> implements Serializable {
private static final long serialVersionUID = 1974679434867091670L;
#Id
#GeneratedValue(generator="idgen", strategy=GenerationType.SEQUENCE)
#Column(name="id")
protected T id;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}
Category class
#Entity
#SequenceGenerator(name="idgen", sequenceName="cat_id_seq", allocationSize=1)
#AttributeOverrides({
#AttributeOverride(name="id", column = #Column(name="cat_id"))
})
#Table(name="categoria")
public class Category extends EntityId<Integer> {
private static final long serialVersionUID = -870288485902136248L;
#Column(name="name")
private String name;
#Column(name="description")
private String description;
}
pom.xml
...
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.15.Final</version>
</dependency>
...
My problem it's similar with this post: https://hibernate.atlassian.net/browse/HHH-12329
From the link you provided. The JPA spec says that:
A sequence generator may be specified on the entity class or on the
primary key field or property. The scope of the generator name is
global to the persistence unit (across all generator types).
So, it's not legal to have two identifier generators with the same name and different configurations. The scope is global, not entity.
To resolve your issue you should push the #Id from the #MappedSuperclass into subclasses.
More details
Edited, added possible workaround:
remove annotation from field in super class;
make getter abstract;
let all sub-classes have their own sequence generator: all generators should have global unique name;
implement getter;
move annotations related to Id field on getter.
public interface EntityId<T extends Serializable> extends Serializable {
public T getId();
public void setId(T id);
}
#Entity
#Table(name="categoria")
public class Category implements EntityId<Integer> {
private static final long serialVersionUID = -870288485902136248L;
#Id
#Column(name="cat_id")
#SequenceGenerator(name="cat_id_gen", sequenceName="categoria_cat_id_seq", allocationSize=1)
#GeneratedValue(generator="cat_id_gen", strategy=GenerationType.SEQUENCE)
private Integer id;
//others attributes here...
#Override
public void setId(Integer id) {
this.id = id;
}
#Override
public Integer getId() {
return this.id;
}
}
I have created a BaseEntity class which will have all the common fields of the other entities like created_date, created_by, updated_by etc., I also kept primary key id in there. I am using extends and using the fields from base entity class. The main problem is id field is of type Long in some entities and type of String in some other entities, so how can I manage the id field?
Base Entity Class:
#Getter
#Setter
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 3779027956207925319L;
protected Long id;
private String createdBy;
private Date createdDate;
private String lastUpdatedBy;
private Date lastUpdatedDate;
private Boolean isActive;
public abstract Long getId();
public abstract void setId(Long id);
#Override
public String toString() {
return String.format(
"BaseEntity [createdBy=%s, createdDate=%s, lastUpdatedBy=%s, lastUpdatedDate=%s, isActive=%s]",
createdBy, createdDate, lastUpdatedBy, lastUpdatedDate, isActive);
}
}
Have a generic base entity, where generic type defines the type of your id column;
#Getter
#Setter
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
protected T id;
// fields, constructors, methods etc
}
When you have an entity where id is of type Long, extend with that type;
#Entity
public class TableWithLongId extends BaseEntity<Long> {
// fields, constructors, methods etc
}
or when you need a String type id;
#Entity
public class TableWithStringId extends BaseEntity<String> {
// fields, constructors, methods etc
}
Modify your getter and setter, use parsing in them so that they get long data and give String data, or vice versa accordingly.