I have a Spring Boot 1.3.5-RELEASE application which is using JPAto Relate my USERS to the ROLES with a Bi-directional ManyToMany relationship.
User
#Table(name = "Users")
#Entity
public class User extends BaseEntity {
#NotEmpty
#Column(unique = true)
private String username;
#NotEmpty
private String password;
#JoinColumn(name = "user_iid")
#OneToMany
private Set<UserRole> userRoles;
//getters and setters
UserRole (intermediary table)
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "user_iid", "role_iid" }))
#Entity
public class UserRole extends BaseEntity {
#RestResource(exported = false)
#ManyToOne
#NotNull
private User user;
#ManyToOne
private Role role;
//getters and setters
Role
#Entity
public class Role extends BaseEntity {
#NotEmpty
#Column(unique = true)
private String name;
#JoinColumn(name = "role_iid")
#OneToMany
private Set<UserRole> userRoles;
//getters and setters
BaseEntity is a class with Ids and Version generator.
Repository
#Repository
public interface Repository extends JpaRepository<Role, String> {
Role findByIid(#Param("iid") final String iid);
When I cURL a localhost:8080/roles/search/findByIid?iid=1 I get a StackOverflow. If the object does not exist, the application respond fine.
I already tried #JsonIgnore but does not work.
Thanks
I got the answer.
I updated the Spring Boot to 1.4.2-RELEASE (which is the last) and everything worked like a charm. I think with the update it updates JPA and Hibernate and make them handle better those ManyToMany relantionships.
Related
I need help for this case.
I have the following entities (I removed getters/setters/hash/toString for easy reading):
#Entity
public class Company implements Serializable{
#Id
private String id;
}
#Entity
public class Document implements Serializable{
#Id
private String id;
}
#Entity
#IdClass(Inbox.PK.class)
public class Inbox implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Company company;
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Document document;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "inbox")
private Invoice invoice;
public class PK implements Serializable{
private Company company;
private Document document;
}
}
First question is, should I use Company and Document types in PK class or String and String?
And here ... the headache :
#Entity
#IdClass(Invoice.PK.class)
public class Invoice implements Serializable {
#Id
#OneToOne(fetch = FetchType.LAZY, mappedBy = "invoice")
// #MapsId // ???
#JoinColumn(name = "companyId")//, referencedColumnName = "company")// ???
#JoinColumn(name = "documentId")//, referencedColumnName = "document")// ???
// #PrimaryKeyJoinColumn // ????
private Inbox inbox;
#Data
public static class PK implements Serializable {
// private Inbox inbox; // ???
// private String company,document; // ???
// private String companyId,documentId; // ???
// private String inboxCompanyId,inboxDocumentId; // ???
}
}
The PK of the Invoice Entity is also the FK to Inbox (I would like constraints to be generated), and the PK of Inbox is composed of two Entities (Company and Document).
I prefer to use IdClass rather EmbeddedId.
How could I configure Invoice to have, at the end, (company_id,document_id) as PK AND FK to Inbox?
I saw your question posted in upwork. I think you should use string + string type fields with #Id and #Column annotations in PK class.
I have three tables each mapping to one of these entities. The 'assigned' table acts as the relationship between 'users' and 'roles' with a foreign key to each table. How would I map this on my entities so that I can get a Set of EntityRoles from the UserEntity? I can't quite figure out how to make this work. Is this even possible?
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id")
private long id;
#Column(name="user_username")
private String username;
#Column(name="user_password")
private String password;
#Column(name="user_email")
private String email;
//I want to be able to get a set of RoleEntities
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id")
private Set<RoleEntity> roles;
}
#Entity
#Table(name = "assigned")
public class AssignedEntity implements Serializable {
#Id
//#Column(name = "assigned_role")
#ManyToOne(targetEntity = RoleEntity.class, fetch = FetchType.LAZY)
#JoinColumn(name = "fk_role")
private long roleId;
#Id
//#Column(name = "assigned_user")
#ManyToOne(targetEntity = UserEntity.class, fetch = FetchType.LAZY)
#JoinColumn(name = "fk_user")
private long userId;
}
#Entity
#Table(name = "roles")
public class RoleEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="role_id")
#OneToOne(fetch = FetchType.LAZY, mappedBy="roleId")
private long id;
#Column(name="role_name")
private String name;
}
You are using an incorrect/inconvenient mapping. Always keep things as simply as possible.
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
}
#Entity
#Table(name = "roles")
public class Role {
#Id
private Long id;
#Column
private String name;
}
A persistent provider will create a (valid) join table for you. You can specify the name of the join table using #JoinTable annotation. Also you will need to think about auto generation values of id for the Role entity: the roles table is something like a reference data table. So, probably, you will need to hardcode the id values.
To get user roles (in the persistent context):
user.getRoles()
I am using spring-boot-dependencies 1.3.5.RELEASE for my application and it runs on Java SE 1.8. I am using Apache Shiro' to mapusergroups inrolestouserpermissionswhereas I am usingDAO(Data Access Object`) for accessing data from database.
Let's say we have an entities such that
Employee "has-a" Department
Department "has-a" Domain
User "has-a" Domain
Entity Classes: Employee
#Entity
#Table(name = "EMPLOYEE")
#Data
#EqualsAndHashCode(of = "id", callSuper = false)
public class Employee {
#Id
#GeneratedValue
private int id;
private String name;
#ManyToOne
#JoinColumn(name="department_id")
private Department dept
}
Entity Classes: Department
#Entity
#Table(name="DEPARTMENT")
#Data
#EqualsAndHashCode(of = "id", callSuper = false)
public class Department {
#Id
#GeneratedValue
private int id;
private String name;
#ManyToOne
#JoinColumn(name="domain_id")
private Domain domain
#OneToMany(mappedBy="department")
private Set<Employee> employees;
}
Entity Classes: Domain
#Entity
#Table(name="DOMAIN")
#Data
#EqualsAndHashCode(of = "id", callSuper = false)
public class Domain{
#Id
#GeneratedValue
private int id;
private String name;
}
Now I would like to restrict User (a login user) to see only those Employees which are associated with the Departments whose Domain has an access to the User. Is there any way to achieve this without changing queries in DAO classes OR to do this with minimum code changes? Thank You.
Which Realm are you using? If you are using the JdbcRealm you should be able to set userRolesQuery to a query of your choice.
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.
I wrote an example for the code i am trying to implement, i get an error with Constraint "Student_Teacher_FK" already exists.
the #embiddable class has a foreign key that is created twice with current code.
#Entity
public class Teacher {
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Entity
public class Student{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Embeddable
public class StudentList implements Serializable {
#ManyToMany
#JoinTable(name = "Student_Teacher",
joinColumns =
#JoinColumn(name = "Student_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "Teacher_ID", referencedColumnName = "ID")
)
#ForeignKey(name = "Student_Teacher_FK", inverseName = "Teacher_Student_FK")
public List<Student> studentList = new ArrayList<Student>();
}
#Entity
public class HistoryTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class LangTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class RetiredTeacher extends Teacher {
// has no students
}
#embeddable : Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity (http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html)
As you are declaring it in 2 different entity, jpa will create associated association table (student-teacher) 2 times with associated fk, which is explicitely named, and so created 2 times too with the same name. Here is your error.
I don't think using #embeddable is appropriated for what you're intending to do. A student has is own existence and is not part of teacher itself (not an uml composition / black diamond) so it's not an embeddable entity. Student list should be held by teacher entity using a simple manyToMany association.