Embedded object inheritance in EJB3 - ejb-3.0

I am developping an user/password system with EJB3.
An user have an embedded password.
And I have two kinds of passwords: user defined or not.
Therefore I have a superclass Password, and its subclass GeneratedPassword. Architecture is indeed debatable.
Here are the "signatures" :
#Entity
#NamedQueries({ //... })
#Table(name="UserAccount")
public class UserAccount implements Serializable {
#Id
#Email
private String email;
#Embedded
private Password password;
public UserAccount(String email) {
this.email = email;
this.password = new GeneratedPassword();
}
// ...
}
#Embeddable
public class Password implements Serializable {
private String encryptedPassword;
// ...
}
#Embeddable
public class GeneratedPassword extends Password {
private String tmpPassword;
// ...
}
Problem is I am having a weird exception (weird because I don't understand it...):
Caused by: javax.persistence.EntityExistsException:
Exception Description: No subclass matches this class [class entities.user.GeneratedPassword] for this Aggregate mapping with inheritance.
Mapping: org.eclipse.persistence.mappings.AggregateObjectMapping[password]
Descriptor: RelationalDescriptor(entities.user.UserAccount --> [DatabaseTable(UserAccount)])
2nd part:
Caused by: Exception [EclipseLink-126] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: No subclass matches this class [class entities.user.GeneratedPassword] for this Aggregate mapping with inheritance.
Mapping: org.eclipse.persistence.mappings.AggregateObjectMapping[password]
Descriptor: RelationalDescriptor(entities.user.UserAccount --> [DatabaseTable(UserAccount)])
So what I understand from these exceptions is that the GeneratedPassword is not recognized as an entity. But if I use the Password class, evrything works fine! So I'm back to the incomprehension state...
Anybody knows how to use embeddable entities within a hierarchy? Is that even the problem???

Specification does not tell anything about inheritance of embeddables, so looks like it is not supported. Probably because of simplicity as target.
Of course some implementations can have it. Unfortunately Hibernate is not one of those: https://hibernate.onjira.com/browse/HHH-1910
Eclipselink supports, but not via annotation or XML descriptor: http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Entities/Embeddable#Inheritance
By the way, question is tagged with hibernate, but you use EclipseLink.

Related

OneToOne Association In JPA not working using Spring boot and spring data JPA

I am trying to implement the OneToOne association in JPA and trying to join two tables using spring boot and spring data JPA. I created one spring boot microservice and implemented the one to one association in my model. But when I am running code I am getting the following error ,
Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a #OneToMany, #ManyToMany or #CollectionOfElement
Here My First model class Users.java is like following,
#Entity
#Table(name = "users")
public class Users implements Serializable {
private static final long serialVersionUID = 9178661439383356177L;
#Id
#Column(name="user_id")
public Integer userId;
#Column(name="username")
public String username;
#Column(name="password")
public String password;
}
And I am testing association by controller using following code,
#GetMapping("/load")
public Users load() {
return (Users) userObj.findAll();
}
Can anyone help to resolve this association issue please ?
This is wrong.
#OneToOne(mappedBy="nuserId")
public Set<UserRoleMapping> roleUserRoleMappingMappingJoin;
}
OneToOne means only one object..right?
See this for mappings understandings.
https://docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/collections.html#collections-persistent
Annotation #OneToOne defines a single-valued association to another entity, and in your case you associate a user to a Set of UserRoleMapping instead of associating it with a single object of that class. Use #ManyToOne annotation
Actually the exception refers to an invalid #OneToMany, #ManyToMany or #CollectionOfElement mapping
and this can only be
#OneToMany()
#JoinColumn(name="nuser_id" , referencedColumnName="nuserId")
public Users nuserId;
If the #OneToMany relation is valid change this at first to
#OneToMany()
#JoinColumn(name="nuser_id" , referencedColumnName="nuserId")
public List<Users> users;
If the #OneToMany relation is NOT valid change this to
#OneToOne()
#JoinColumn(name="nuser_id" , referencedColumnName="nuserId")
public Users users;

Spring Data JPA native query result binding

Entity class:
#Entity
#SqlResultSetMapping(
name="hourMapping",
classes=#ConstructorResult(
targetClass=Representation.class,
columns={
#ColumnResult(name="hour", type=BigDecimal.class),
#ColumnResult(name="transactions", type=BigDecimal.class)
}
)
)
#NamedNativeQuery(name="MyEntity.reportByHour", query="SELECT hour,SUM(tran_per_hour) AS transactions FROM MY_ENTITY GROUP BY hour ORDER BY hour"
,resultSetMapping="hourMapping")
#Table(name="MY_ENTITY")
public class MyEntity implements Serializable {
Pojo class:
#Data //Lombok
#JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class Representation {
public Representation(BigDecimal hour, BigDecimal transactions) {
this.hour = hour;
this.transactions = transactions;
}
private BigDecimal hour;
private BigDecimal transactions;
Repository interface:
public interface MyEntityRepository extends JpaRepository<MyEntity, MyEntityPK> {
List<Representation> reportByHour();
}
When I run the endpoint which invokes the native query, I get exception:
Failed to convert from type [java.lang.Object[]] to type [com.representation.Representation] for value '{0, 198}'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.math.BigDecimal] to type [com.representation.Representation]
Now if I just have "hour" field returned from my native query (and relevant changes to POJO constructor etc) it works fine.
Any help appreciated.
Ok, false alarm. My hibernate dependencies were all messed up and causing conflicts so resulting in the above exception.
After fixing these dependency issues, works great!!
Long story short: let spring-boot-* handle most hibernate dependencies instead of overriding or managing your own.

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 JPA and Generics

I have an entity that looks like this
#Entity(name = "encounter_pdf_export")
public class EncounterPDFExport<T extends Encounter> implements Serializable {
public static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long pdfExportId;
#Any(metaColumn = #Column(name = "encounter_type"))
#Cascade(CascadeType.ALL)
#AnyMetaDef(
idType = "long",
metaType = "string",
metaValues = {
#MetaValue(value = "FooEncounter", targetEntity = FooEncounter.class)
})
#JoinColumn(name = "encounter_id")
private T encounter;
The abstract type that I'm extending is:
public abstract class Encounter {
public abstract Long getEncounterId();
}
Here is my Spring Data Repository
#Repository
public interface EncounterPDFExportRepository extends PagingAndSortingRepository<EncounterPDFExport, Long> {
EncounterPDFExport findOneByEncounter_encounterId(#Param("encounterId") Long encounterId);
}
I am getting a stack trace when starting up the application related to to the findOneByEncounter_encounterId method:
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [encounter] on this ManagedType [com.iimassociates.distiller.domain.EncounterPDFExport]
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.checkNotNull(AbstractManagedType.java:144)
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.getAttribute(AbstractManagedType.java:130)
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:468)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.getTypedPath(JpaQueryCreator.java:300)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:243)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:148)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:88)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:46)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:109)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:116)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$CountQueryPreparer.<init>(PartTreeJpaQuery.java:237)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:65)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:100)
I am assuming that either Spring Data JPA doesn't support abstracted/generic fields? If that's the case, would creating a #Query be a sufficient workaround?
Not sure if this will be helpful to anyone, but I did get this working.
Removed the abstract class and made it an interface with a single public getEncounterId() method
Modified FooEncounter to implement the above interface
Removed generics from the EncounterPDFExport class
Modified the encounter field to utilize the above interface rather than a generic
Apparently, I'm hitting some Hibernate bug/limitation when accessing fields within FooEncounter. Accessing Encounter within EncounterPDFExport works OK, though. I modified my Spring Data JPA Repository to look like the following (note the modification from finding by encounter.encounterId vs. just encounter):
#Repository
public interface EncounterPDFExportRepository extends PagingAndSortingRepository<EncounterPDFExport, Long> {
EncounterPDFExport findOneByEncounter(#Param("encounter") Encounter encounter);
}
The Hibernate bug in question seems to be related to https://jira.spring.io/browse/DATAJPA-836.

Spring Data JPA with MVC repository

I am building a Spring repository for some JPA-annotated entities. I have created a repository:
public interface AppRepository extends PagingAndSortingRepository<App, String>
{
}
The App class looks as follows:
#Entity
public class App implements Serializable
{
#Id
private String appId;
#OneToMany(mappedBy = "app")
private List<AgentUser> agentusers;
#OneToMany(mappedBy = "app")
private List<AppFacet> appfacets;
// getters and setters go here
}
where the AgentUser and the AppFacet hold a reference property called app towards an App object. In the AgentUser class, I have changed the RestResource rel:
#Entity
public class AgentUser
{
...
#ManyToOne
#JoinColumn(name = "AppId")
#RestResource(rel = "agentUserToApp", exported = false)
private App app;
// other properties go here
}
I am getting the following error message while querying the /apps path:
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain: org.springframework.hateoas.PagedResources["_embedded"]);
Do you know what could be causing it? Please note that I only have one App object in a database, for testing purposes and no other kind of object.
Update
The trace is:
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:677)
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2240)
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:231)
org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:161)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:167)
And after that, a lot of:
org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:352)
org.springframework.data.rest.webmvc.mapping.LinkCollectingAssociationHandler.doWithAssociation(LinkCollectingAssociationHandler.java:101)
The problem resides in that, whenever you have links to some entities, you must implement a repository for that entity too, in order to generate the proper links.

Resources