Hibernate returns proxy for base class - java

I have a hierarchy in my domain model, which is described by classes:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class BaseEntity {
#Id
private Long id;
// other fields
}
#DiscriminatorValue(value = "Individual")
public class IndividualEntity extends BaseEntity {
// fields
}
#DiscriminatorValue(value = "Branch")
public class BranchEntity extends BaseEntity {
// fields
}
I'm fetching objects like this:
Specification<BaseEntity> specification = createSpecification();
BaseEntity entity = baseRepository.findOne(specification);
(I'm using spring-data)
The problem is that Hibernate returns proxy object (what I understand), but the proxy is of BaseEntity, not the proper subclass (its' class is BaseEntity_$$_jvsted9_26, therefore entity instanceof IndividualEntity is false).
What is interesting, not all objects are returned as proxy.
I'm fetching entities in loop (common transaction), some of them are returned in normal form (i.e. IndividualEntity/BranchEntity), some as proxies.
If I change mechanism, so that every fetch is done in separate transaction - no proxy objects are returned at all.
I know that I can unwrap that proxy (e.g. like here), but what is the reason for such behaviour (kinda strange for me) and can I avoid it?

Can't be certain without seeing more of the object model, but one reason Hibernate would do this is if the BaseEntity had already had to be resolved as a proxy for the same BaseEntity.id earlier in the session.
For example, if there is another class that has a ToOne relation to a BaseEntity, it will just have a foreign key to the id, so will use a BaseEntity_$$... proxy to delay resolving the correct subclass for the other end. This then becomes the instance for that id that is managed in the Hibernate PersistenceContext.
Clearly a Hibernate.unwrap(), or one of the other options in the link above will reveal the 'true' instance. One other option is to use abstract methods on BaseEntity (e.g. isIndividual()). This can look a bit tidier, but ultimately Hibernate will still need to resolve the proxy when the method is called.

Related

How to make Spring (Hibernate) instantiate child entities in single table inheritance scenario as their proxies instead of base entity proxy?

Recently I started getting Hibernate's HHH000179: Narrowing proxy to class warning when attempting to delete child entites (inheritance) that contain other relations.
This led me to learning that Hibernate proxies are created for each entity separately, even if such entity is an abstract base entity (not #MappedSuperclass, but just abstract #Entity) - that is even if base entity class will never exist on its own.
Consider structure of attributes:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(...)
public abstract class Attribute { ...ids and common fields... }
#Entity
#DiscriminatorValue("V")
public class AttributeValued extends Attribute
{
#OneToMany( mappedBy = "attribute",
cascade = CascadeType.ALL,
orphanRemoval = true)
private Set<Value> values = new LinkedHashSet<>();
}
With Spring JPA i have repository such as:
interface AttributeRepository extends JpaRepository<Attribute, Long> {}
Consider given some id that is known to be of type AttributeValued I run this:
Attribute a = this.attributeRepository.getReferenceById(id);
if (a instanceof AttributeValued)
{
System.out.println("VALUED");
}
else
{
System.out.println("OTHER");
}
This prints 'OTHER' while I'd expect it to print VALUED.
That means that Spring/Hibernate instantiated #getReferenceById to base class Attribute (or rather it's Hibernate proxy).
Is there a way for Spring/Hibernate to return proxy of actual entity (AttributeValued) when using common JpaRepository<Attribute>?
Because of this behivior if I do something like this:
// id is known to be of type AttributeValued
Attribute a = this.attributeRepository.getReferenceById(id);
this.attributeRepository.delete(a);
Hibernate will strike me with HHH000179: Narrowing proxy to class AttributeValued because variable a is proxy of Attribute while delete(a) will create another representation of the same row as proxy of AttributeValued, because of AttributeValued.values relation having:
cascade = CascadeType.ALL
orphanRemoval = true
So now I have proxy of Attribute and proxy of AttributeValued (I think).
You can use #Embedded annotation on Child entity so that child and parent entities will form a single table in database
You can read more about this in official documentation https://www.baeldung.com/jpa-embedded-embeddable
Okay, so problem was absolutely elsewhere than I thought.
#getReferenceById is JpaRepository method.
which I used basically everywhere in my whole project (since Hibernate is Jpa and I assumed it's the correct way).
Turns out that querying with it is for some reason not polymorphic and returns proxy of Attribute, even if row is known to represent AttributeValued (with #DiscriminatorValue("V")).
#findById(id) is a CrudRepository method and it actually is polymorphic and returns proxy of AttributeValued, thus when I use #delete(id) later, I will not get problems like in original question (two proxies for the same row and warning HHH000179: Narrowing proxy to class).

How to prevent Spring JPA Entities from becoming optional when saving to CRUDRepository?

I was trying to learn Spring Framework and ran into a problem with saving entities into CRUD Repository. I had few Entities with automatic numeric ID generation and they work just fine, but then I tried to make a class with String being a primary key just like this:
#Entity
#Table(name = "USERS")
#Builder
public class User {
#Id
#Column(name = "USER_NAME", nullable = false)
#Getter #Setter
private String name;
#Column(name = "USER_PASS", nullable = false)
#Getter #Setter
private String pass;
}
First I was getting exceptions about this class not having a default constructor:
org.springframework.orm.jpa.JpaSystemException: No default constructor for entity: : com.company.Model.User; nested exception is org.hibernate.InstantiationException: No default constructor for entity: : com.company.Model.User
Already weird, but still I decided to change #Builder annotation into 2 constructors, one with both arguments and second with none. I tried to save the entity instance into CRUD Repository userDAO (which is nothing more than interface extending CRUDRepository) by the typical test:
User admin = new User("admin", "6aDcZ72k");
...
#Test
public void saveUserAndFindById() {
admin = userDAO.save(admin);
assertThat(userDAO.findById(admin.getName())).isEqualTo(admin);
}
The result was assertion failed because the saved entity had "Optional" type:
org.junit.ComparisonFailure:
Expected :com.company.Model.User#2c06b113
Actual :Optional[com.company.Model.User#2c06b113]
I know I'm doing something really wrong but can't figure this out. Or maybe there is a way to just prevent making it optional? There must be few other entities with the reference on this class, but these references obviously don't work because of the above issue.
First of all,jpa require the entity has a No Arguments Constructor cause it will create a instance first and then populate it.The easiest way is to add #NoArgumentsConstructor that offered by lombok on the entity class.
And then,Optional is used by spring data jpa in order to avoid NullPointException and in fact it be is useful actually.If you want to use the interface that Spring-data-jpa offered,then you have to use Optional too.You cloud look here for more info about Optional:link1,link2
By the way,I usually use it like:
ExampleEntity example=exampleRepository.findById(id).orElseThrow(()->new ExampleNotFoundException());
In this way,you dont need to deal with Optional or think about NullPointException.
or:
ExampleEntity example=exampleRepository.findById(id).orElse(null);
In this way if you cant find the target entity,then it will be null.So dont forget to check if the entity is null.
Hope it could help~~~
It is not your save(...) that is returning Optional but userDAO.findById(admin.getName(). According to the documentation, CrudReposiotry provides a findById() whose return type is Optional<T>.
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
Optional<T> findById(ID primaryKey);
}
If you do not want Optional as return type, You will need to provide your own method to do that. For example:
public interface PeronRepository extends CrudRepository<Person, String> {
Person findById(String personId);
}

Choosing Inheritance Strategy - Hibernate

if this question was aksed here, i surely couldnt find it, or it didnt particulary help me.
i've read some tutorials and some questions for Inheritance Mapping, which couldnt quitely solve my questions.
Say i have an abstract class :
User
And 3 more other subclasses :
UserA, UserB, UserC
those all extend User.
Every Subclass has its own table, Superclass User, meanwhile, doesn't.
In my other class Website i have a ArrayList, or should i say Collections, of Users.
The list should fetch all users of the Website.
Which strategy should i use ? I thought of MappedSuperclass, but since in my Website class the List is of User type, so am not sure what to do here.
Thanks for any help!
With JPA the Java implementation always depends on you own preferences and requirements, sometimes it is the matter of a choice.
Yes, #MappedSuperclass will do.
You can have every child with unidirectional relationship to Website. Then you gonna have Website object inside your User class (with a bunch of annotations), which will map to a database as foreign_key field (presume you are using SQL storage and 'Repositories' DAO abstraction from JPA).
It is not necessary to store a collection of users inside Website class. Just think if you really need it - it can be a mess to support consistency.
But there are cases where you need bidirectional relationship. When you store objects in memory (for caching purposes for example) you'll probably need to have this collection. In this case why not to have 'User' collection? You will fetch data through dedicated repositories(or even if you're not using those, any other way will be using 'User' tables with foreign_key, not the 'Website' table) anyway.
So, for example with the use of Spring Data JPA you can define a unidirectional relationship in a superclass and use 'repositories' next way(and bidirectional example you can find anywhere in the internet, so I am not providing it):
#Entity
public class SuperUser extends User {
...
}
#Entity
public class BasicUser extends User {
...
}
#MappedSuperclass
public abstract class User implements Serializable {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "website_uuid", nullable = false)
protected Website website;
...
}
#Entity
public class Website implements Serializable {
...
}
#Repository
public interface SuperUserRepository extends CrudRepository<SuperUser, Long> {
Iterable<SuperUser> findByWebsite(Website website);
}
#Repository
public interface BasicUserRepository extends CrudRepository<BasicUser, Long> {
Iterable<BasicUser> findByWebsite(Website website);
}
What you are asking for seems a typical "Table-per-concrete-class" inheritance strategy. https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#entity-inheritance-table-per-class
In older version of the user guide, it has mentioned that separate table will be mapped for each non-abstract classes. In the latest document the "non-abstract" part is not mentioned but I believe it still works similarly.
So it looks something like:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class User {...}
#Entity
class UserA extends User {...}
#Entity
class UserB extends User {...}
#Entity
class UserC extends User {...}
But you should be aware of this inheritance strategy usually gives inefficient query as internally it is using union.

Any way to get fields from entity without actually joining into that table?

I'm not sure this is possible, but knowing just the very basics of JPA, I want to ask if it is possible. Basically I have an entity (We'll call it MyEntity) with a bunch of fields on it. We now want a 2nd entity that has all the same fields as MyEntity plus some of it's own. The use case for this is archiving these entities. We want to store all the archived entities in a separate table than MyEntity so that we don't have to qualify all the queries with archived=false. The JPA annotations for MyEntity look something like this:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public abstract class MyEntity
{
....
There are multiple classes that extend this abstact class, each with #DiscriminatorValue annotations
For my archived entity (MyArchivedEntity) I want something along the lines of this:
#Entity
public class MyArchivedEntity
{
private MyEntity entity;
private String archiveSpecificField;
....
The problem with this of course is that it will want to join into the MyEntity table and get a specifc MyEntity record for populate the entity field. Is there some kind of annotation or something I can do to just get the same fields/columns from that entity (MyEntity) into this entity (MyArchivedEntity)?
Like I said in the beginning, I'm not sure if this is possible, but I hope I've explained well enough the end goal of what I'm trying to achieve, so that there could be some way to achieve it. If it makes any difference, I'm using PostgreSQL with EclipseLink.
What you can do is using #MappedSuperclass on a AbstractParentEntity becoming the super class of both MyEntity and MyArchiveEntity. So you will have something like the following:
#MappedSuperclass
public abstract class AbstractParentEntity {
public String someField;
...
}
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public abstract class MyEntity extends AbstractParentEntity
{
//here you don't have any field (they all move to AbstractParentEntity
// (or, at least all the fields that need to be archivied are now declared in parent class)
....
}
#Entity
public class MyArchivedEntity extends AbstractParentEntity
{
private String archiveSpecificField;
....
More about MappedSuperclass here:
Mapped superclass inheritance allows inheritance to be used in the object model, when it does not exist in the data model. It is similar to table per class inheritance, but does not allow querying, persisting, or relationships to the superclass. Its' main purpose is to allow mappings information to be inherited by its' subclasses. The subclasses are responsible for defining the table, id and other information, and can modify any of the inherited mappings. A common usage of a mapped superclass is to define a common PersistentObject for your application to define common behavoir and mappings such as the id and version. A mapped superclass normally should be an abstract class. A mapped superclass is not an Entity but is instead defined though the #MappedSuperclass annotation or the <mapped-superclass> element.
You may wish to look into EclipseLink's history support. It can automatically maintain a historical archive table.
See,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/History
Another option would be to map the same classes in another persistence unit using an orm.xml to the archive tables.

Exclude field in JPA Entity Listener

I have an entity class in my Enterprise Java application that has an entity listener attached to it:
#Entity
#EntityListeners(ChangeListener.class)
public class MyEntity {
#Id
private long id;
private String name;
private Integer result;
private Boolean dirty;
...
}
However, I would like it so that the entity listener got triggered for all fields except the boolean one. Is there any way exclude a field from triggering the entity listener without making it transient?
I'm using Java EE 5 with Hibernate.
However, it is possible if you implement your own solution. I've had the same need for audit log business requirement, so designed my own AuditField annotation, and applied to the fields to be audit-logged.
Here's the example in one entity bean - Site.
#AuditField(exclude={EntityActionType.DELETE})
#Column(name = "site_code", nullable = false)
private String siteCode;
So, the example indicates the 'siteCode' is a field to audit log, except DELETE action. (EntityActionType is an enum and it contains CRUD operations.)
Also, the EntityListenerhas this part of code.
#PostPersist
public void created(Site pEntity) {
log(pEntity, EntityActionType.CREATE);
}
#PreUpdate
public void updated(Site pEntity) {
log(pEntity, EntityActionType.UPDATE);
}
#PreRemove
public void deleted(Site pEntity) {
log(pEntity, EntityActionType.DELETE);
}
Now what it has to do in log() is, to figure what fields are to audit log and what custom actions are involved optionally.
However, there's another to consider.
If you put the annotation at another entity variable, what fields of the entity have to be logged? (i.e. chained logging)
It's your choice whether what are annotated with #AuditField only in the entity or some other ways. For my case, we decided to log only the entity ID, which is a PK of a DB table. However, I wanted to make it flexible assuming the business can change. So, all the entites must implement auditValue() method, which is coming from a base entity class, and the default implementation (that's overridable) is to return its ID.
There is some kind of mixing of concepts here. EntityListeners are not notified about changes in attribute values - not for single attribute, neither for all attributes.
For reason they are called lifecycle callbacks. They are triggered by following lifecycle events of entity:
persist (pre/post)
load (post)
update(pre/post)
remove (pre/post)
For each one of them there is matching annotation. So answer is that it is not possible to limit this functionality by type of persistent attributes.

Categories

Resources