Which hibernate entity inheritance strategies is required - java

My base entity that is common part for many other entity:-
#MappedSuperclass
public abstract class IdBase {
#GeneratedValue
#Column(name = "id")
private Long id;
#Version
private Long version;
#CreatedBy
#Column(name = "created_by")
private String createdBy;
#CreatedDate
private Instant created;
#LastModifiedBy
#Setter(AccessLevel.PRIVATE)
#Column(name = "updated_by")
private String updatedBy;
#LastModifiedDate
#Setter(AccessLevel.PRIVATE)
private Instant updated;
}
One of the entity as follows:-
#Entity
#Table(name="TBL_SUB_EMPLOYEES")
public class SubEmployeeEntity extends IdBase {
#Column(name="sub_title")
private String subTitle;
#Column(name="sub_role")
private String subRole;
}
My generic repository as:-
#Repository
public interface AuditRepository<E extends IdBase> extends JpaRepository<E, Long>, JpaSpecificationExecutor<E> {
}
When I try to query SubEmployeeEntity by the generic repository I got error:-
Unable to locate Attribute with the the given name [subTitle] on this
ManagedType [com.test.IdBase]; nested exception is
java.lang.IllegalArgumentException: Unable to locate Attribute with
the the given name [subTitle] on this ManagedType [com.test.IdBase]
IdBase is common class for many entity and I kept only the common column here. I only showed SubEmployeeEntity. I have same kind of entity inherited form IdBase as well. Why is it looking for subTitle in IdBase. How do I fulfill my requirements?

Seems like you are using JPA specifications and within the specification try to access subtype properties although the repository you are using has the type bound IdBase. To make this work, you would have to subclass the repository for every concrete type and also inject that subtype of repository which I guess you don't want to do. If that's the case, you should use TREAT in your specifications to access the subtype properties.
You have to imagine that the type variabbe has no effect as Spring can never observe what you use on your use-site. Only if create a subtype and inject that subtype, Spring can resolve the type variable to the concrete subtype. This leads to the fact that Spring creates a JPA root for the type bound IdBase so now you have to treat the alias to be able to access properties through JPA Criteria.

Related

Using POJO as a base for hibernate entity

I started developing in Java quite recently, and my client is also a developer who is developing in Java since it was released.
So when he says "we have a good reason why don't we use transient fields in our project", I didn't ask what those reasons are. But, back to the question:
I have two classes:
POJO, which is used solely to generate JSON:
public class BaseSector implements Serializable {
private String id;
private String name;
private String parentId;
Entity:
public class Sector {
#Column(length = 36)
private String id;
#Column(length = 40)
private String name;
#Column(length = 36)
private String parentId;
// ... Bunch of other fields
Is there any way for an Entity class to extend this POJO, and add Column annotations dynamically? Or have POJO as an interface? Or use entity class in POJO constructor?
Earlier we had something like this:
for (Sector sector : sectors) {
BaseSector baseSector = new BaseSector();
baseSector.setId(sector.getId());
baseSector.setName(sector.getName());
baseSector.setParentId(sector.getParentId());
}
But I changed that by using BaseSector in HQL constructor...
Btw, we also have SectorInfo and SimpleSectorInfo which also extend BaseSector, but that's a different subject..
A TRANSIENT field tells your ENTITY class that this particular field should not be persisted in the DB. #Transient annotation is used to ignore a field to not persist in database in JPA, where as transient key word used to ignore a field from serialization. The field annotated with #Transient still can be serialized, but the field declared with transient keyword not to be persisted and not to be serialized.
A POJO can be extended by an ENTITY and vice-versa. This is stated in JPA specification.You can find more examples at the below links :
Link:1 : JPA Non-Entity SuperClass
Link 2 : JPA Specification
You can achieve this by using an annotation : #javax.persistence.MappedSuperclass
It states : A superclass is treated as non-entity class if no mapping related annotations such as #Entity or #MappedSuperclass are used on the class level.
This means your superclass will be treated as a non-entity class here if you do not use the above annotations in your superclass.
How to Construct the classes :
SUPERCLASS which also a POJO for your JSON object
#MappedSuperclass
public class BaseSector implements Serializable {
private String id;
private String name;
private String parentId;
}
ENTITY class :
#Entity
#Table(name = "sector")
public class Sector extends BaseSector {
#Column(length = 36)
private String id;
#Column(length = 40)
private String name;
#Column(length = 36)
private String parentId;
// ... Bunch of other field
}
You can also override some property defined by BaseSector in your ENTITY - Sector
You need to use
#AttributeOverride // for single property
#AttributeOverrides // override more than one property

xHow can I have #Id string for CrudRepository in Spring with Spring Data JPA?

The problem is that I am getting an exception using #RepositoryRestResource for my UserRepository that extends JpaRepository.
The reason for that is that findById is only accepting Long or Int types by default, even I have
#Id String id; and not #Id Int id in my entity definition.
I have tried searching StackOverflow and Google, but haven't found any solutions.
The error message is as follows:
"Failed to convert from type [java.lang.String] to type [java.lang.Integer] for value '3175433272470683'; nested exception is java.lang.NumberFormatException: For input string: \"3175433272470683\""
I want to make it work with a
#Id String id;
Any suggestions?
Many thanks in advances. It's a big privilege to ask questions here.
The Entity class:
#Entity // This tells Hibernate to make a table out of this class
#Table(name = "users")
public class XmppUser {
#Id
private java.lang.String username;
private String password;
private String serverkey;
private String salt;
private int iterationcount;
private Date created_at;
// #Formula("ST_ASTEXT(coordinates)")
// #Column(columnDefinition = "geometry")
// private Point coordinates;
// private Point coordinates;
private String full_name;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "username", nullable = true)
private XmppLast xmppLast;
You must change the type of the ID type parameter in the repository to match with the id attribute type on your entity.
From the Spring docs:
Interface Repository<T,ID>
Type Parameters:
T - the domain type the repository manages
ID - the type of the id of the entity the repository manages
Based on
#Entity // This tells Hibernate to make a table out of this class
#Table(name = "users")
public class XmppUser {
#Id
private java.lang.String username;
//...
}
It should be
public interface UserRepository extends CrudRepository<XmppUser, String> {
//..
}
See:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html#findById(ID)
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/Repository.html
You could try something like this:
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Column(name = "PR_KEY")
private String prKey;
If you want to read more about this subject you could begin looking throw here or here
according to the latest version of spring data jpa(2.1.10 GA), you can use it like
here is the link
JpaRepository is a special case of CrudRepository. Both JpaRepository and CrudRepository declare two type parameters, T and ID. You will need to supply these two class types. For example,
public interface UserRepository extends CrudRepository<XmppUser, java.lang.String> {
//..
}
or
public interface UserRepository extends JpaRepository<XmppUser, java.lang.String> {
//..
}
Notice that the second type java.lang.String must match the type of the primary key attribute. In this case, you cannot specify it as String or Integer, but it is java.lang.String instead.
Try not to name a customized class as String. It is a bad practice to use the same class name as already present in the JDK.
I think there is an approach to solve this problem.
Let's say, Site is our #Entity.
#Id
private String id;
getters setters
then you can invoke findById as follow
Optional<Site> site = getSite(id);
Note: this worked for me, I hope it will help someone.

Conditional load of relations using spring data jpa

I'm developing a multi language application and My tables are designed for this purpose as well. for example I have a Country class like this:
#Entity
#Table(name = "province")
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "country_id", nullable = false)
private Country country;
#OneToMany
#JoinColumn(name = "province_id")
private List<ProvinceTranslation> translations;
}
#Entity
#Table(name = "province_translation")
public class ProvinceTranslation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private Language language;
#ManyToOne
#JoinColumn(name = "province_id")
private Province province;
}
I want that translations field load only translation with specified language, and country field load translation with specified language too(Country class has a list of CountryTranslation obviously!). I don't want to write queries and I want that spring data jpa load relations with the language that I specify explicitly.
It seems, a little bit of writing JPA query is required in this case.
Because the countryTranslation class is missing I put the focus on the Province class.
The Language class is also unknown, maybe this is an enum one, like this:
public enum Language {UNKNOWN, GERMAN, ENGLISH, SPAIN}
To avoid loading of all translations of the entity you have to select the translation according to the given language while fetching the entities from database. I prefer to do this with utilizing spring repositories (which I hope you already have involved). In the ProvinceRepository declared like this
public interface ProvinceRepository extends CrudRepository<Province, Long> {
...
}
you have to provide the required find- or count methods.
To get a List of all provinces with specific translation you may declare a function like this one, inside the ProvinceRepository:
#Query("SELECT new org.your.package.goes.here.Province(p.id, p.country, pt.name) FROM Province p inner join p.translations pt where pt.language = ?1")
List<Province> findAllWithTranslation(Language language);
To make this working, there must a exist a constuctor that accepts the three parameters id, country, name. The name parameter may be set to a new translation property of the Province class, respectively the created province object. If the Language class is indeed an enum class the #Enumerated annotation must be added to the language field.
Nevertheless, I am convinced that the provision of translation strings should be better done with the help of an internationalization library (i18n).

How to inherit from multiple base abstract classes in JPA

I faced a problem how I can create JPA entity which extends multiple base abstract classes (without creating additional table). I know there is an annotation #MappedSuperclass, but it gives an ability to create only one base class as soon as we use extends and multiple inheritance is not a Java feature.
For example, I have two bases:
#MappedSuperclass
public abstract class Authored {
#ManyToOne
private User user;
}
and
#MappedSuperclass
public abstract class Dated {
private String creationDate;
}
I expect that some of my models will extend only Authored, some -- only Dated, and some -- both.
Though it's only possible to write
#Entity
public class MyEntity extends Authored {
...
}
or
#Entity
public class MyEntity extends Dated {
...
}
Some discussions propose to inherit classes in line (e.g. Authored and AuthoredAndDated) but this seems too dirty, none of this bases logically can't extend another one.
ADDITION
I have to note that this style is supported in other frameworks like Django (due to multiple inheritance in python) so it's strange that JPA doesn't support it.
I am sorry to disappoint you, but there is no other solution than creating AuthoredAndDated as you suggested.
We faced in the same issue for our entities and went with the same procedure.
We have a
#MappedSuperclass
public class TimestampedObject {
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at")
private Date createdAt;
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "updated_at")
private Date updatedAt;
}
and a
#MappedSuperclass
public class IdObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id", updatable = false, columnDefinition = "serial")
private Long id;
}
Thus we created a TimestampedIdObject for this purpose.
Edit:
If you find another suitable solution, it would be great if you could post it here, as we have the same issue...
You should use an #Embeddable / #Embedded for goal by replacing inheritance with composition.
First, do not use #MappedSuperClass, but use #Embeddable instead for your classes you want to share the attributes with:
#Embeddable
public class Authored {...}
#Embeddable
public class Dated {...}
In the next step your Entity should not inherit from Authored or Dated but instead get an attribute referencing them:
#Entity
public class MyEntity {
#Embedded
private Authored authored;
#Embedded
private Dated dated;
}
If you want to get behaviour out of this, where you can generically access without those new attributes, you would need to introduce an interface exposing the necessary methods.
For expample if MyEntity should be able to provide details on last updates and creation, you would introduce an interface Authorable which defines to methods to access the relevant data.
public interface Authorable { /* necessary methods */ }
MyEntity will implement this interface then:
#Entity
public class MyEntity implements Authorable {
/* previous content plus implemented mehtods from interface */
}

What's wrong with my custom type for JPA/Hibernate?

My custom type is (no default constructor!):
package com.XXX.common;
public class Email implements Serializable {
private String e;
public Email(String str) {
e = str;
}
}
My entity in Hibernate 3.5.6:
package com.XXX.persistence;
import com.XXX.common;
#Entity
#TypeDef(
name = "email",
defaultForType = Email.class,
typeClass = Email.class
)
public class User {
#Id private Integer id;
#Type(type = "email")
private Email email;
}
Hibernate says:
org.hibernate.MappingException: Could not determine type for:
com.XXX.common.Email, at table: user, for columns:
[org.hibernate.mapping.Column(email)]
What am I doing wrong?
My custom type is (no default constructor!) (...)
A custom type must implement one of the interfaces from org.hibernate.usertype (implementing UserType would be enough for your specific example), your Email class is NOT a custom type. In other words, you'll need to create some EmailType class that Hibernate will use to persist a field or property of type Email.
PS: There is IMO not much value at wrapping a String with a class but let's say it was an example.
References
Hibernate Core Reference Guide
5.2.3. Custom value types
Resources
Hibernate CompositeUserType and Annotations
user_type examples on the Hibernate Wiki
typeClass must point to something that extends UserType (this handler class contains the mapping from and to the database).

Categories