How to map existing JPA entities to PicketLink - jpa

I am trying to migrate a Seam 2 app to CDI and use PicketLink for security. After all the reading and researching, it seems like all the examples are having one to one mapping between PicketLink model and the backend entity. e.g. Account to AccountEntity, Partition to PartitionEntity. Since I already have entities in place representing identity model, I am stuck on trying to map them to PicketLink. Here is what I have:
#MappedSuperClass
public class ModelEntityBase implement Serializable {
#Id #Generated
Long id;
Date creationDate;
}
#Entity
public Account extends ModelEntityBase {
String username;
String passwordHash;
#OneToOne(mappedBy = "account")
Person person;
}
#Entity
public Person extends ModelEntityBase {
String name;
String email;
#OneToOne
#JoinColumn(name = "account_id")
Account account;
}
Two entities (plus a super class) representing a single identity model in PicketLink, e.g. stereo type User.
Based on this why IdentityType id is String not Long, I tried to add a new Entity in:
#Entity
#IdentityManaged(BaseIdentityType.class);
public class IdentityTypeEntity implement Serializble {
#Id #Identifier
private String id;
#OneToOne(optional = false, mappedBy = "identityType")
#OwnerReference
private Account account;
#IdentityClass
private String typeName;
#ManyToOne #OwnerReference
private PartitionEntity partition;
}
I've tried a few different ways with the annotation and model classes. But when using IdentityManager.add(myUserModel), I just can't get it to populate all the entities. Is this even possible?

Got help from Pedro (PicketLink Dev). Post the answer here to help others.
This is the model class I ended up using.
#IdentityStereotype(USER)
public class User extends AbstractAttributedType implements Account {
#AttributeProperty
private Account accountEntity;
#AttributeProperty
#StereotypeProperty(IDENTITY_USER_NAME)
#Unique
private String username;
#AttributeProperty
private boolean enabled;
#AttributeProperty
private Date createdDate;
#AttributeProperty
private Date expiryDate;
#AttributeProperty
private Partition partition;
// getter and setter omitted
}
And created a new entity to map to this model:
public class IdentityTypeEntity implements Serializable {
#Id
#Identifier
private String id;
#OneToOne(optional = false, mappedBy = "identityType",
cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#AttributeValue
// #NotNull
private HAccount accountEntity;
#IdentityClass
private String typeName;
#ManyToOne
#OwnerReference
private PartitionEntity partition;
#AttributeValue
private String username;
#AttributeValue
// #Transient
private boolean enabled;
#AttributeValue
private Date createdDate;
#AttributeValue
private Date expiryDate;
}
PL can map property with #AttributeProperty to entity property with #AttributeValue. But it can only map to one entity. Therefore there is no way to map, say User and its properties over to Account and Person. But you can have the entity (in my case accountEntity) in the model. I also have to duplicate a few fields in the new IdentityTypeEntity and my existing Account entity (username, eanbled, createdDate) because PL requires these. Use a #PrePersist and similar to sync them.

Related

JPA how to set foreign id - in Embeddable id - that is generated

My use case is: I have a Product with multi language name. To have at most one name per language a translation should be identified by locale + productId.
My problem is to get it working with a generated productId. This are the entities:
e.g. in oracle i get: "ORA-01400: cannot insert null into ."PRODUCT_NAME"."FOREIGN_ID""
Product:
#Entity
#Data
public class Product {
#Id
#GeneratedValue
private Long id;
#Column(unique = true)
private String articleNumber;
/**
* Localized name of the product.
*/
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#MapKey(name = "nameId.locale")
private Map<Locale, ProductName> names = new HashMap<>();
}
ProductName:
#Entity
#Data
public class ProductName {
#EmbeddedId
private TranslationId nameId;
private String name;
}
TranslationId:
#Embeddable
#Data
public class TranslationId implements Serializable {
private static final long serialVersionUID = 3709634245257481449L;
#Convert(converter = LocaleConverter.class)
private Locale locale;
private Long foreignId; //<-- this is null, should reference product
}
Is there a way to get this working without having to save the product first without the name? Without a generated id it is working of course - i just set same id for both.
I would like to re-use the translation id for other translated fields of other entities.

duplicate results when sorting by collection property

Repost from here
Given entities and repository:
#Entity
public final class Partner {
#Id
private String id;
#OneToMany(mappedBy = "partner", fetch = FetchType.LAZY)
private List<Merchant> merchants;
...
}
#Entity
public final class Merchant {
#Id
private String id;
#Column
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private Partner partner;
...
}
public interface PartnerRepository
extends JpaRepository<Partner, String>, QueryDslPredicateExecutor<Partner> {
}
If there is only one partner having two merchants in the DB then the following code incorrectly returns list with two instances of the same parnter.
partnerRepository.findAll(new Sort("merchants.name"));
This is caused internally by the DB join. By creating custom implementation that adds the distinct to the selection the result is correctly the single partner.
Wouldn't it be correct to do distinct selection per default?
Try
#OneToMany(mappedBy = "partner", fetch = FetchType.LAZY)
#OrderBy("name")
private List<Merchant> merchants;

Spring Data JPA auditing fails when persisting detached entity

I've setup JPA auditing with Spring Data JPA AuditingEntityListener and AuditorAware bean. What I want is to be able to persist auditor details even on entities with predefined identifiers.
The problem is that when JPA entity with predefined id is being persisted and flushed it's auditor details cannot be persisted:
object references an unsaved transient instance - save the transient instance before flushing: me.auditing.dao.AuditorDetails
The interesting part is that when an entity with a generated id is saved - everything's fine. In both cases the entities are new. I could not pinpoint the problem digging through hibernate code so I've created a sample project to demonstrate this (test class me.auditing.dao.AuditedEntityIntegrationTest) It has both entities with predefined and generated identifiers and should be audited.
The entities are:
#Entity
public class AuditedEntityWithPredefinedId extends AuditableEntity {
#Id
private String id;
public String getId() {
return id;
}
public AuditedEntityWithPredefinedId setId(String id) {
this.id = id;
return this;
}
}
and:
#Entity
public class AuditedEntityWithGeneratedId extends AuditableEntity {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
public String getId() {
return id;
}
public AuditedEntityWithGeneratedId setId(String id) {
this.id = id;
return this;
}
}
where parent class is:
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity implements Serializable {
private static final long serialVersionUID = -7541732975935355789L;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#CreatedBy
private AuditorDetails createdBy;
#CreatedDate
private LocalDateTime createdDate;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#LastModifiedBy
private AuditorDetails modifiedBy;
#LastModifiedDate
private LocalDateTime modifiedDate;
And the auditor getter implementation is:
#Override
public AuditorDetails getCurrentAuditor() {
return new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
}
Edit 2016-08-08: It seems that when a new entity with predefined id is saved, it gets two different instances of createdBy and modifiedBy AuditorDetails, which is quite logical if the entity wouldn't be actually new. So, a completely new entity with generated gets both AuditorDetails of same instance, and the one with manually set id doesn't. I tested it by saving auditor details in AuditorAware bean before returning it to AuditingHandler.
Ok, so for now the only solution I could find is to actually persist AuditorDetails before writing it to audited entities like so:
#Override
#Transactional
public AuditorDetails getCurrentAuditor() {
AuditorDetails details = new AuditorDetails()
.setId(null)
.setUserId("someUserId")
.setDivisionId("someDivisionId");
return auditorDetailsRepository.save(details);
}
It is not the most elegant solution, but it works for now.

Load data from two OneToMany relationships

I want to display data from a database with JPA on the frontend, i.e. display the Person's details. Details can be 0..n Adresses and 0..m Phones. The entities are shown below:
#Entity
public class Person implements Serializable {
#Id #GeneratedValue
private int id;
private String name;
#OneToMany(mappedBy="person")
private List<Address> addresses = new ArrayList<Address>();
#OneToMany(mappedBy="person")
private List<Phone> phones = new ArrayList<Phone>();
// plus getter and setter
}
#Entity
public class Address implements Serializable {
#Id #GeneratedValue
private int id;
#ManyToOne
private Person person;
private String onestring; // plus getter and setter
}
#Entity
public class Phone implements Serializable {
#Id #GeneratedValue
private int id;
#ManyToOne
private Person person;
private String anotherstring; // plus getter and setter
}
As lazy loading is activated, ...
#PersistenceContext
private EntityManager em;
public Person getPerson(int id) {
return em.find(Person .class, id);
}
... would only provide proxies on adresses and phones.
Questions:
What is good way displaying the all data on the frontend, i.e. the person and all its addesses and phones? (Except for setting FetchType to EAGER).
Is there a way to fetch both addresses and phones into the same instance of Person or do I have to fetch the Person twice (one time with addresses, one time with phones) to omit a cartesian product?

Mapping JPA entity relationships

I would like some advice on how to best layout my JPA entity classes. Suppose I have 2 tables I would like to model as entities, user and role.
Create Table users(user_id primary key,
role_id integer not null )
Create table role(role_id primary key,
description text,
)
I create the following two JPA Entities:
#Entity
#Table(name="users")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long userId;
private Long roleId;
private Role role;
#Column(name = "user_id")
#Id
public Long getUserId() {}
#Column(name = "role_id")
public Long getRoleId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
}
Role Entity:
#Entity
#Table(name="Role")
#Access(AccessType.PROPERTY)
public class Role implements Serializable {
private String description;
private Long roleId;
#Column(name = "role_id")
#Id
public Long getRoleId() {}
#Column(name = "description")
public Long getDescrition(){}
#ManyToOne()
#JoinColumn(name="role_id")
public Role getRole() {}
}
Would the correct way to model this relationship be as above, or would I drop the private Long roleId; in Users? Any advice welcomed.
When I map it this way, I receive the following error:
org.hibernate.MappingException: Repeated column in mapping for entity:
Yes, you would drop the private Long roleId mapping when you have a #ManyToOne on the same column.
As the error implies, you can only map each column in an #Entity once. Since role_id is the #JoinColumn for the #ManyToOne reference, you cannot also map it as a property.
You can, however, add a convenience method to return the role ID, like
public Long getRoleId() {
return role.getId();
}