Hibernate, Extending Entity - "java.lang.IllegalArgumentException: Unknown entity" - java

I have a Hibernate Entity, BaseEvent, which works fine:
import javax.persistence.*;
#Entity
#Table(name = "base_event")
#SequenceGenerator(name = "seq", allocationSize = 1, sequenceName = "seq")
public class BaseEvent
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
#Column(name = "id")
protected Long id = null;
#Column(name = "my_field", nullable = false)
protected String myField;
public Long getId()
{
return id;
}
public void setId(final Long id)
{
this.id = id;
}
public String getMyField()
{
return myField;
}
public void setMyField(final String myField)
{
this.myField = myField;
}
}
I want to identify when this object is changed and update some Map in my application. The easiest way I could think of doing this was to extend BaseEvent and override the setter:
import java.text.MessageFormat;
public class ExtendedEvent extends BaseEvent
{
#Override
public void setMyField(final String myField)
{
System.out.println(MessageFormat.format("Setting myField to {0}", myField));
super.setMyField(myField);
}
}
This works fine in my application, but then when I come to persist the Entity, Hibernate complains it doesn't know what an ExtendedEvent is.
java.lang.IllegalArgumentException: Unknown entity: my.package.ExtendedEvent
I can see that extending Hibernate Entities is a non-trivial problem, especially when you start adding fields - but all I want is for Hibernate to treat ExtendedEvent as a BaseEvent (because it is). Is there a simple solution for this?

Make base event #MappedSuperclass and extending class #Entity
so
#MappedSuperclass
public class BaseEvent
and
#Entity
#Table(whatever)
public class ExtendedEvent extends BaseEvent

If you want to update your map only when changes are updated in the data store, I would recommend implementing onFlushDirty in a Hibernate Interceptor. This method gets called whenever the Session is flushed to the database for every entity change. You can check the object type in the onFlushDirty method for your entity of interest and property of interest.

Related

#Formula not working in hibernate with object

I have a enum of few status value
NEW, REVIEWD, PUBLISHED, PENDING, UPDATED, SPAM, DUPLICATE, IRRELEVANT, UNPUBLISHED
I don't want to use them as enumerated so created one entity for that. For convenient I want to keep a column in entity to initialize status from enum and convert that enumerated value to a Object of status entity. for this..
I have two entity. I want to refer a column with value from another entity.
Basically I want to initialize a object with formula.
Entities are
#Entity
#Table(name = "event_status")
public class EventStatus {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="eventStatusId")
private Integer eventStatusId;
#Enumerated(EnumType.STRING)
#Column(unique = true,name="eventStatusType")
private EventStatusType eventStatusType;
public EventStatus() {
this(EventStatusType.NEW);
}
public EventStatus(EventStatusType eventStatusType) {
super();
this.eventStatusType = eventStatusType;
}
public Integer getEventStatusId() {
return eventStatusId;
}
public EventStatusType getEventStatusType() {
return eventStatusType;
}
public void setEventStatusId(Integer eventStatusId) {
this.eventStatusId = eventStatusId;
}
public void setEventStatusType(EventStatusType eventStatusType) {
this.eventStatusType = eventStatusType;
}
}
I have another entity in which I am referring object of this entity
#Entity
#Table(name = "event_")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Event implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id_")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Transient
public EventStatusType eventStatusType = EventStatusType.NEW;
#ManyToOne(fetch = FetchType.EAGER, targetEntity = EventStatus.class)
#Formula("select * from event_status where eventStatusId= 1")
private EventStatus status;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public EventStatus getStatus() {
System.out.println("Event.getStatus() " + status);
return status;
}
public void setStatus(EventStatus status) {
System.out.println("Event.setStatus()");
this.status = status;
}
}
This is not giving any exception but not initializing this value.
Is it possible to initialize this EntityStatus with value of eventStatusType in Event entity
I would like to explain that based on the documentation:
5.1.4.1.5. Formula
Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also create some kind of virtual column. You can use a SQL fragment (aka formula) instead of mapping a property into a column. This kind of property is read only (its value is calculated by your formula fragment).
#Formula("obj_length * obj_height * obj_width")
public long getObjectVolume()
The SQL fragment can be as complex as you want and even include subselects.
...
5.1.7.1. Using a foreign key or an association table
...
Note
You can use a SQL fragment to simulate a physical join column using the #JoinColumnOrFormula / #JoinColumnOrformulas annotations (just like you can use a SQL fragment to simulate a property column via the #Formula annotation).
#Entity
public class Ticket implements Serializable {
#ManyToOne
#JoinColumnOrFormula(formula="(firstname + ' ' + lastname)")
public Person getOwner() {
return person;
}
...
}
Also, we should use insertable = false, updatable = false, because such mapping is not editable

Get Id of the associated object after save

There is simple model of associated entities:
MainModel class
#Entity
#Table(name = "main_model")
public class MainModel
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "id_main_model", referencedColumnName = "id")
private List<AssociatedModel> am_collection;
public MainModel() { }
public long getId()
{
return this.id;
}
public void addAssociated(AssociatedModel am)
{
am_collection.add(am);
}
}
AssociatedModel class
#Entity
#Table(name = "associated_model")
public class AssociatedModel
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
public AssociatedModel() { }
public long getId()
{
return this.id;
}
}
Simplest jpa repository for MainModel
public interface MainModelDAO extends JpaRepository<MainModel, Long>
{
}
and finally, controller's method for creating and saving AssociatedModel instance
#RequestMapping("/url")
public String createAssociated(#RequestParam("id_mainmodel") long id_mainmodel, #RequestBody AssociatedModel newAm)
{
MainModel mm = MainModelDAOobject.findOne(id_mainmodel);// MainModelDAOobject - #Autowired
mm.addAssociated(newAm);
MainModelDAOobject.saveAndFlush(mm);
return String.valueOf(newAm.getId());// get Id of associated saved object
}
Associated obect saves into database correctly, but id value of this object, allowed by its getId() method, always zero. How can I get Id of the saved associated object correctly?
Try calling MainModelDAOobject.flush() after saving the value, but before reading the ID.
See this answer for an explanation.
Edit: try manually saving newAm by adding a new line:
mm.addAssociated(newAm); // Existing line
newAm = MainModelDAOobject.save(newAm); // New line
MainModelDAOobject.save(mm); // Existing line
you need a sequencegenerator:
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MainModel SequenceGenerator")
#SequenceGenerator(name = "MainModel SequenceGenerator", sequenceName = "MainModel Sequence", initialValue = SEQUENCE_INITIAL_VALUE, allocationSize = SEQUENCE_ALLOCATION_SIZE)
Thanks to David Levesque for helpful suggestion.
problem is solved as follows:
need additional repository for AssotiatedModel
public interface AssotiatedModelDAO extends JpaRepository {}
then just save associated object manually:
mm.addAssociated(newAm);
newAm = AssotiatedModelDAOobject.save(newAm);
newAm = MainModelDAOobject.save(newAm)`;
MainModelDAOobject.save(mm);

MappedSuperclass - Change SequenceGenerator in Subclass

I'm using JPA2 with Hibernate and try to introduce a common base class for my entities. So far it looks like that:
#MappedSuperclass
public abstract class BaseEntity {
#Id
private Long id;
#Override
public int hashCode() {
// ...
}
#Override
public boolean equals(Object obj) {
// ...
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
}
However, for every table theres a sequence $entityname_seq which I want to use as my sequence generator. How can I set that from my subclass? I think I need to override #GeneratedValue and create a new SequenceGenerator with #SequenceGenerator.
Yes, it is possible. You can override the default generator name with the #SequenceGenerator annotation.
Base class
#MappedSuperclass
public abstract class PersistentEntity implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "default_gen")
protected Long id = 0L;
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
}
Sequence (SQL)
create sequence role_seq;
Derived class
#Entity
#Table(name = "role")
#SequenceGenerator(name = "default_gen", sequenceName = "role_seq", allocationSize = 1)
public class Role extends PersistentEntity implements Serializable
{
private static final long serialVersionUID = 1L;
#NotNull
#Size(max = 32)
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
This approach worked fine in Hibernate 4.1.x, but it didn't in EclipseLink 2.x.
edit
As per the comment, it seems to be working with EclipseLink 2.6.1-RC1.
In JPA that cannot be done with annotations. Annotation itself cannot be overridden. Entity inherits all the mapping information from MappedSuperClass. There is only two annotations that can be used to redefine mappings inherited from mapped superClass:
AttributeOverride to override column mappings and
AssociationOverride to override join columns / table.
Neither of them helps with GeneratedValue.
With EclipseLink, you can use a Customizer. DescriptorCustomizer interface defines a way to customize all the information about a jpa descriptor (aka a persistent entity).
public class SequenceCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
descriptor.setSequenceNumberName(descriptor.getTableName());
}
}
and in your mapped superclass:
#MappedSuperclass
#Customizer(SequenceCustomizer.class)
public abstract class AbstractEntity implements Serializable {
...
}
I'm writing this as it gets too unreadable as the comment on the accepted answer:
I have a BaseEntity that every other Entity inherits from:
BaseEntity.java:
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ID")
private Long id;
I then have two Entities User and Order that both inherit from BaseEntity whilst also having the #SequenceGenerator annotation:
User.java:
#SequenceGenerator(name = "SEQ_ID", sequenceName = "SEQ_USER", allocationSize = 1)
public class User extends BaseEntity { ... }
Order.java:
#SequenceGenerator(name = "SEQ_ID", sequenceName = "SEQ_ORDER", allocationSize = 1)
public class Order extends BaseEntity { ... }
It works on H2 at least with 2 Sequences SEQ_USER and SEQ_ORDERS:
select SEQ_USER.nextval from dual;
select SEQ_ORDERS.nextval from dual;

Hibernate, single table inheritance and using field from superclass as discriminator column

I have following kinds of classes for hibernate entity hierarchy. I am trying to have two concrete sub classes Sub1Class and Sub2Class. They are separated by a discriminator column (field) that is defined in MappedSuperClass. There is a abstract entity class EntitySuperClass which is referenced by other entities. The other entities should not care if they are actually referencing Sub1Class or Sub2Class.
It this actually possible? Currently I get this error (because column definition is inherited twice in Sub1Class and in EntitySuperClass) :
Repeated column in mapping for entity: my.package.Sub1Class column: field (should be mapped with insert="false" update="false")
If I add #MappedSuperClass to EntitySuperClass, then I get assertion error from hiberante: it does not like if a class is both Entity and a mapped super class. If I remove #Entity from EntitySuperClass, the class is no longer entity and can't be referenced from other entities:
MappedSuperClass is a part of external package, so if possible it should not be changed.
My classes:
#MappedSuperclass
public class MappedSuperClass {
private static final String ID_SEQ = "dummy_id_seq";
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = ID_SEQ)
#GenericGenerator(name=ID_SEQ, strategy="sequence")
#Column(name = "id", unique = true, nullable = false, insertable = true, updatable = false)
private Integer id;
#Column(name="field", nullable=false, length=8)
private String field;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
#Entity
#Table(name = "ACTOR")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="field", discriminatorType=DiscriminatorType.STRING)
abstract public class EntitySuperClass extends MappedSuperClass {
#Column(name="description", nullable=false, length=8)
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
#Entity
#DiscriminatorValue("sub1")
public class Sub1Class extends EntitySuperClass {
}
#Entity
#DiscriminatorValue("sub2")
public class Sub2Class extends EntitySuperClass {
}
#Entity
public class ReferencingEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
#Column
private Integer value;
#ManyToOne
private EntitySuperClass entitySuperClass;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public EntitySuperClass getEntitySuperClass() {
return entitySuperClass;
}
public void setEntitySuperClass(EntitySuperClass entitySuperClass) {
this.entitySuperClass = entitySuperClass;
}
}
In my project it is done this way:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "field", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("dummy")
public class EntitySuperClass {
// here definitions go
// but don't define discriminator column here
}
#Entity
#DiscriminatorValue(value="sub1")
public class Sub1Class extends EntitySuperClass {
// here definitions go
}
And it works. I think your problem is that you needlessly define discriminator field in your superclass definition. Remove it and it will work.
In order to use a discriminator column as a normal property you should make this property read-only with insertable = false, updatable = false. Since you can't change MappedSuperClass, you need to use #AttributeOverride:
#Entity
#Table(name = "ACTOR")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="field", discriminatorType=DiscriminatorType.STRING)
#AttributeOverride(name = "field",
column = #Column(name="field", nullable=false, length=8,
insertable = false, updatable = false))
abstract public class EntitySuperClass extends MappedSuperClass {
...
}
You can map a database column only once as read-write field (a field that has insertable=true and/or updatable=true) and any number times as read-only field (insertable=false and updatable=false). Using a column as #DiscriminatorColumn counts as read-write mapping, so you can't have additional read-write mappings.
Hibernate will set value specified in #DiscriminatorColumn behind the scenes based on the concrete class instance. If you could change that field, it would allow modifying the #DiscriminatorColumn field so that your subclass and value in the field may not match.
One fundamental: You effectively should not need to retrieve your discriminator column from DB. You should already have that information within the code, of which you use in your #DiscriminatorValue tags. If you need read that from DB, reconsider carefully the way you are assigning discriminators.
If you need it in final entity object, one good practice can be to implement an Enum from discriminator value and return store it in a #Transient field:
#Entity
#Table(name="tablename")
#DiscriminatorValue(Discriminators.SubOne.getDisc())
public class SubClassOneEntity extends SuperClassEntity {
...
#Transient
private Discriminators discriminator;
// Setter and Getter
...
}
public enum Discriminators {
SubOne ("Sub1"),
SubOne ("Sub2");
private String disc;
private Discriminators(String disc) { this.disc = disc; }
public String getDisc() { return this.disc; }
}

Hibernate MappingException

I'm getting this Hibernate error:
org.hibernate.MappingException: Could not determine type for:
a.b.c.Results$BusinessDate, for columns: [org.hibernate.mapping.Column(businessDate)]
The class is below. Does anyone know why I'm getting this error??
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"businessDate"
})
#XmlRootElement(name = "Results")
#Entity(name = "Results")
#Table(name = "RESULT")
#Inheritance(strategy = InheritanceType.JOINED)
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Results implements Equals, HashCode
{
#XmlElement(name = "BusinessDate", required = true)
protected Results.BusinessDate businessDate;
public Results.BusinessDate getBusinessDate() {
return businessDate;
}
public void setBusinessDate(Results.BusinessDate value) {
this.businessDate = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"raw",
"display"
})
#Entity(name = "Results$BusinessDate")
#Table(name = "BUSINESSDATE")
#Inheritance(strategy = InheritanceType.JOINED)
public static class BusinessDate implements Equals, HashCode
{
....
Update: This code was generated by HyperJaxB. So I don't claim to understand it all, just trying to make some changes to it!
Update2: Here's the full (yah it's big) src file
Using a static nested class as a field type is fine and supported. But Hibernate won't know how to map such a complex type to a column type (which is what the error message says).
So you'll need either to create a user type to handle this or to annotate the Results.BusinessDate field with a #OneToOne annotation to persist it in another table (I would also remove the #Inheritance which is useless but this is not the problem here).
Update: Just to clarify, using a user type or mapping the complex type with #OneToOne does work. The following code works perfectly (tested):
#Entity
public class EntityWithStaticNestedClass implements Serializable {
#Id
#GeneratedValue
private Long id;
#OneToOne
private EntityWithStaticNestedClass.StaticNestedClass nested;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public EntityWithStaticNestedClass.StaticNestedClass getNested() {
return nested;
}
public void setNested(EntityWithStaticNestedClass.StaticNestedClass nested) {
this.nested = nested;
}
#Entity
public static class StaticNestedClass implements Serializable {
#Id
#GeneratedValue
private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
}
And both entities get well persisted in their respective tables. But you're not showing the entire code nor the exact error so I can't say why it didn't for you (maybe you're missing #Id etc).
That being said, if you don't want businessDate to be persisted at all, annotate it with #Transient (with JPA, fields are persistent by default):
Update: You can't mix field and property access. So you need to annotate getBusinessDate() with #Transienthere. Sorry, I couldn't guess that from the shown code and I thought it would be obvious.
Same comment as Kevin Crowell. You might also look at not using inner classes for entity types. I've actually never seen someone do that with Hibernate, so I'm not sure if it's even possible, or how you would map it.
The #Inheritance annotation on the BusinessDate inner class seems a little fishy too - the inner class is static, and does not inherit from another entity, unless Hibernate treats inner classes as "inherited."
Overall, not really sure what you're trying to accomplish, but you might be making your life harder than it should be. I would recommend not using inner classes, and just mapping all the entities in a more simple/straightforward fashion.

Categories

Resources