MappedSuperclass produces 'has an unbound type and no explicit target entity' - spring

I have this entity. It should work on both, internal and external, users.
#Entity(name = "TokenAuthentication")
class TokenAuthenticationEntity<T>(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
...
#NotNull
#ManyToOne(fetch = FetchType.EAGER)
val user: T,
) : BaseEntity()
When I run this, I get
Property TokenAuthenticationEntity.user has an unbound type and no explicit target entity. Resolve this Generic usage issue or set an explicit target attribute
So Hibernate tells me to f*** off with my generics, it needs explicit definitions. Any of you have an idea how to get this running with generics?

This is not possible. The reason being T type erasure at compile-time. This T type is used at compile-time to check types and guarantee type safety, but then it is removed. Having said that, each instance of TokenAuthenticationEntity can have a different value for T, and as explained before this information is lost.
What you can actually do is the following:
#MappedSuperclass
public abstract class TokenAuthenticationEntity<T> {
private T user;
}
Now you can create your entities based on this generic superclass:
#Entity
public class InternalUser extends TokenAuthenticationEntity<IUser> { }
#Entity
public class ExternalUser extends TokenAuthenticationEntity<EUser> { }
Why? Because each concrete subclass of TokenAuthenticationEntity has a type T which is defined and is retained and unique inside the subclasses. In this case, JPA will store your subclasses in one or more tables, depending on the chosen #InheritanceStrategy (if the Inheritance annotation is not specified or if no inheritance type is specified for an entity class hierarchy, the SINGLE_TABLE mapping strategy is used). Additional details at https://www.baeldung.com/hibernate-inheritance#single-table.

Related

How to add extra parameter to all queries in Repository

I'm letting system to create queries from the methods names:
public interface CompanyRepository extends JpaRepository<Company, Long> {
Boolean existsByName(String name);
Boolean existsByRegCode(String regCode);
}
There are several different repos for different entities (Company, User, Shop) and they all have field named CountryId.
Now I need to add condition "AND CountryId = :CountryId" to all queries in all repos , where the country parameter gets it's value from some configuration file.
I know that I should build some base class for this and extend from that, but can't figure out what to put into this class.
You can define a superclass for all of your entities and add the countryId field to this superclass and annotate with #Where(clause = "countryId='id'")
#Where(clause = "countryId='id'")
public class Entity {...}
and
public class Company extends Entity {...}

Does Room support entity inheritance?

I am trying to migrate our project to use Room, which, by the way, I think is an awesome step forward.
I have the following structure:
public class Entity extends BaseObservable {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "_id", typeAffinity = ColumnInfo.INTEGER)
private long mId;
#ColumnInfo(name = "is_dirty")
#TypeConverters(BooleanTypeConverter.class)
private boolean mIsDirty;
// default constructor and accessors omitted for brevity
}
#Entity(tableName = "some_entities")
public class SomeEntity extends Entity {
#ColumnInfo(name = "type", typeAffinity = ColumnInfo.TEXT)
private String mType;
#ColumnInfo(name = "timestamp", typeAffinity = ColumnInfo.INTEGER)
private long mTimestamp;
// constructor, accessors
}
When I try to compile my project, it fails with no specific error.
If I try to compile it with a flat entity hierarchy, all is well.
So, my main question is:
Does Room support entity inheritance? Will it be able to get the column definitions from the parent Entity class?
I would also like to know if extending BaseObservable (which I need to get the Data Binding working) can cause problems with Room? BaseObservable has one private transient field, so maybe this is causing some issues with the code generation.
Are there any recommended patterns to deal with this, or will I just have to flatten my entity hierarchy?
After further investigation it turns out that Room Entities should not extend the BaseObservable class. It contains fields that can't be marked with #Ignore and break the code generation.
Room works well with inheritance. The annotations are processed as expected and the DB operations behave normally. You can extend from both an Entity and a POJO.

Activiti JPA support (activiti-spring-boot-starter-jpa) unable to detect generic primary key type

I'm working on a project in which our JPA Entities inherit org.springframework.data.jpa.domain.AbstractPersistable, which looks as below:
#MappedSuperclass
public abstract class AbstractPersistable<PK extends Serializable> implements Persistable<PK> {
#Id #GeneratedValue private PK id;
...
And our domain classes are defined as below:
public class User extends AbstractPersistable<Long> {
...
}
So, eventually, all our primary keys are Long. However, when I'm trying to start a process instance with a domain object, I'm getting this error:
org.activiti.engine.ActivitiException: Error while evaluating expression: ${reviewer}
...
Caused by: org.activiti.engine.ActivitiIllegalArgumentException: Unsupported Primary key type for JPA-Entity: java.io.Serializable
at org.activiti.engine.impl.variable.JPAEntityMappings.createId(JPAEntityMappings.java:168)
at org.activiti.engine.impl.variable.JPAEntityMappings.getJPAEntity(JPAEntityMappings.java:120)
...
So, looks like activiti-spring-boot-starter-jpa won't work when the domain classes inherit from a base class which has generic primary key, or I'm missing something?
Looking at the source, the only supported ID types are primitives.
There is a comment in the code that goes:
<snip>
Class<?> type = metaData.getIdType();
// According to JPA-spec all primitive types (and wrappers) are supported, String, util.Date, sql.Date,
// BigDecimal and BigInteger
</snip>
By using a generic, the code is passing through a serializable that is not properly matched.
Seems this would be a relatively easy override in the JPAEntityMappings class.

spring data rest hateoas dynamically hide repository

I'm still trying to figure what exactly it is I am asking but this is fallout from a discussion in the office. So the dilemma is that on a mapping set to eager with a repository defined for the entity the mapping is to, a link is produced. Some of the time that is fine but some of the time I'd rather have the object fetched itself. If there is not a repository defined for that entity then that is what will occur with the eager fetch strategy. What would be ideal is if I could pass in a parameter and have the existence of that repository disappear or reappear.
Not totally following, but either the repo exists or not. If you want to be able to access entities of type X independently of other entity types, then you have to define a repo for type X.
I think you could achieve something similar using projections.
So you define define a repository for your association entity. By default spring data rest will just render a link to this entity and not embed it in the response.
Then you define a projection with a getter for your associated entity. You can choose on the client side if you want the projection by adding the projection query parameter.
So lets say you have a person with an address - an exported repository exists for Person and Address:
#Entity
public class Person {
#Id #GeneratedValue
private Long id;
private String firstName, lastName;
#OneToOne
private Address address;
…
}
interface PersonRepository extends CrudRepository<Person, Long> {}
interface AddressRepository extends CrudRepository<Address, Long> {}
Your projection could look like this:
#Projection(name = "inlineAddress", types = { Person.class })
interface InlineAddress {
String getFirstName();
String getLastName();
Address getAddress();
}
And if you call http://localhost/persons/1?projection=inlineAddress you have the address embedded - and by default it is just linked.

ObjectTypeConverter not found within persistence unit

In my project I use an enum in some entities. The enum is to be stored in the database as an integer. To achieve this I use EclipseLink's ObjectTypeConverter.
I'd like to use annotations since I use Spring to omit the persistence.xml. The annotation to configure the ObjectTypeConverter must be specified on an entity. I don't feel the need to specify the annotation on all classes that use this enum as this is redundant and not maintainable. Specifying it once on some entity would work but this doesn't make sense in an OOP design (or any design for that mater). A solution would be to annotate the enum with #ObjectTypeConverter, but this doesn't work since the enum isn't an entity.
Example that isn't working but would be ideal:
#Entity
public class ExampleEntity
{
#Id
private Long id;
#Convert("exampleenum")
private ExampleEnum ee;
}
#ObjectTypeConverter(name = "exampleenum", objectType = ExampleEnum.class, dataType = Integer.class,
conversionValues =
{
#ConversionValue(objectValue = "A", dataValue = "100"),
#ConversionValue(objectValue = "B", dataValue = "200"),
#ConversionValue(objectValue = "C", dataValue = "300")
})
public enum ExampleEnum
{
A, B, C;
}
Example results in the following exception:
Exception Description: The converter with name [exampleenum] used with the element [field ee] in the class [class com.example.ExampleEntity] was not found within the persistence unit. Please ensure you have provided the correct converter name.
Since I'm using Spring, JPA and EclipseLink I accept any answer using these frameworks.
Upon reading the documentation (which I should have done more carefully in the first place) I noticed:
An ObjectTypeConverter must be be uniquely identified by name and can be defined at the class, field and property level and can be specified within an Entity, MappedSuperclass and Embeddable class.
I couldn't annotate the enum with #Entity (as this requires a table) or #MappedSuperclass (as this doesn't make sense), but #Embeddable would make sense in a way. Marking the enum with #Embeddable did the trick:
#Embeddable
#ObjectTypeConverter(...)
public enum ExampleEnum
...

Resources