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;
}
}
Related
What I want to achieve to take auto generated ID, hash it and save it into other field in the class, but at the stage of creating object by constructor ID is not yet generated. Any workaround ideas?
#Entity
public class MyClass {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
String hashID;
MyClass(){
this.hashID = Utils.hashID(id);
}
//setters and getters
}
```
One way that I can think of is you can use an entity lifecycle callback event like #PostLoad which is called when the entity is loaded in the persistence context, and initialize your hashed field from the id.
E.g.
#Entity
public class MyClass {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
String hashID;
#PostLoad
public void postLoad() {
// Here id is initialized
this.hashID = Utils.hashID(id);
}
}
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 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.
I have an entity class which inherits from four level of inheritance in which the top level parent defines the primary key (#Id) and I'm having trouble figuring out what I did wrong as I get this error:
Entity class [class D] has no primary key specified. It should define either an #Id, #EmbeddedId or an #IdClass. If you have defined PK using any of these annotations then make sure that you do not have mixed access-type (both fields and properties annotated) in your entity class hierarchy.
Here's the hierarchy:
A->B->C->(Entity)D
This is my non-entity class that gives the values to its children:
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
#MappedSuperclass
public class A implements Serializable {
#Id
#GeneratedValue
protected Long id;
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
protected Date deleted;
public Date getDeleted() {
return deleted;
}
public void setDeleted(Date deleted) {
this.deleted = deleted;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
and this is one of its child :
#MappedSuperclass
public abstract class B extends A implements Serializable {
}
B->C
import javax.persistence.Entity;
import javax.persistence.Transient;
#MappedSuperclass
public class C extends B{
protected String name;
protected String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
And finally C-> (Entity)D
#Entity
public class D extends C{
private String codeD;
public String getCodeD(){return codeD;}
public String setCodeD(String codeD) {this.codeD = codeD;}
}
According to every source I've found, normally with the #MappedSuperclass and implementing Serializable it should work. Thought I've tried implementing Serializable even every step of the hierarchy but I received the same error message.
I'm currently using Payara 4.1.1.1621. I don't know if that might be the problem as I've seen this kind of error in Payara on some thread but they all miraculously resolved themselves.
I resolved this error by adding my base class to persistence.xml.
The configuration seems to be as the specification suggests.
The only thing that comes to my mind that can be added is the #Column annotation to explicitly declare the database column names:
#Id
#GeneratedValue
#Column(name = "id")
protected Long id;
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
#Column(name = "deleted")
protected Date deleted;
Try it out. If it still doesnt work try to annotate all the fields in the classes marked as #MappedSuperClass.
Also, class C should have its fields marked as protected not private?
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