JPA, duplicate entry on persist in join table when using #ManyToMany relationship - jpa

I want to create custom JAAS authentication where my users and principals relationships are defined in JPA as shown:
class AuthUser
public class AuthUser implements Serializable {
// own properties
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int uid;
#Column(name="NAME",unique=true)
private String name;
// Join classes
#ManyToMany(fetch=FetchType.EAGER,cascade={CascadeType.MERGE})
#JoinColumn(name="PRINCIPALS_PRINCIPAL")
private Set<AuthPrincipal> principals;
}
class AuthPrincipal
public class AuthPrincipal implements Serializable {
// Defining roles
public enum Principal {
AUTHUSER, STUDENT, ADMINISTRATOR, TEACHER
}
#Id
#Enumerated(EnumType.STRING)
#Column(name="PRINCIPAL")
private Principal principal;
#ManyToMany(mappedBy = "principals")
#JoinColumn(name="USERS_USER")
private Set<AuthUser> users;
}
Maps to the following Table definition
Table authprincipal
===================
PRINCIPAL varchar(255) PK
Table authuser
==============
UID int(11) PK
EMAIL varchar(255)
NAME varchar(255)
PASSWORD varchar(255)
Table authuser_authprincipal
============================
users_UID int(11) PK
principals_PRINCIPAL varchar(255) PK
Now, I created a JSF file from which I call an action method that calls this one:
public void createUser(AuthUser newUser) throws UserNameExistsException, UserEmailExistsException {
AuthPrincipal role = authRoleFacade.find(AuthPrincipal.Principal.AUTHUSER);
if( role == null ){
role = new AuthPrincipal();
role.setPrincipal(AuthPrincipal.Principal.AUTHUSER);
authRoleFacade.create(role);
}
authUserFacade.create(newUser);
addPrincipalToUser(newUser, role);
}
The actual problem
I can create the first user. But I can't create the second user . Notice that at the second user I use the existing role object, and only cascade a merge operation.
The strangest thing is that it says it duplicates the 2-AUTHUSER key where 2 is the id of the new user so cannot already be in the database. What is wrong with it or with Eclipselink or with Me?
The error what eclipselink throws
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-AUTHUSER' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO AUTHUSER_AUTHPRINCIPAL (principals_PRINCIPAL, users_UID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="principals" sql="INSERT INTO AUTHUSER_AUTHPRINCIPAL (principals_PRINCIPAL, users_UID) VALUES (?, ?)")

This was all My fault. I was not careful enough :)
1) The problem occured because of my fault, though, I don't know why the error log talked about user id 2 when I only had one user, but i'm gonna tell a possible reason
The problem was: I wanted to persist an already persisted user. In my #SessionScoped #ManagedBean I created an AuthUser in the constructor. In the first registration it got persisted but I did not create a fresh one. When I wanted to register the next one what my program actually did was: changed the username, email and password of the already persisted AuthUser and wanted to persist it again.
Back to 1) I can imagine when I called persist the second time, Eclipselink actually persisted my Entity updating the user id to 2 in both the AuthUser table and the join table. Afterwards because I defined the Merge operation on AuthUser.principals, it wanted to update again the join table and that's when it messed up. If I had looked closely to the generated Queries in the log file, I think I could have figured it out myself.
I got the hint here: http://www.eclipse.org/forums/index.php/m/692056/#msg_692056

Related

Hibernate postgres auto increment after manual insert

I have a basic spring application, with a simple entity. I have a flyway script, to create the postgres table, and add some starting data.
create table user (
id serial primary key,
username varchar (50) unique not null,
password varchar (150) not null
);
insert into user (id, username, password) values (1, 'name', 'somehashed');
insert into etc...
I've set up my entity as follows:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, columnDefinition = "serial")
private Long id;
...
other fields, constructor, getters setters etc...
My problem is, that on start-up, the basic entities are persisted by flyway, but upon trying to save a new entity, hibernate tries to give it the ID 1, although it is already given to another one.
I tried it also with SEQUENCE strategy, the problem didn't get solved.
Ok, problem was that I specified explicitly the ID I wanted to give while the insert script, and I didn't let postgres do the magic...

Using JPA CompositeKey uses a property from another Entity

I tried searching but was having problems finding what I want. I have the following schema( * indicates primary key)
USER
*UserId
-Desc
Registration
*DeviceId
*UserId
-date
So I want to create a primary key for Registration like...
#Embeddable
public class RegPk{
private String deviceId;
private User user;
#Column(name="DEV_ID")
public String getDevId(){
return deviceId;
}
...
#ManyToOne
#JoinColumn(name="USER_ID", referencedColumnName="USER_ID")
public User getUser() {
return user;
}
...
}
Is this right? Will it update the USER_ID field properly update in registration when I persist?
When I try this kind of thing out I get the following....
[10/7/13 13:37:07:156 EDT] 000000ae webapp E com.ibm.ws.webcontainer.webapp.WebApp logServletError SRVE0293E: [Servlet Error]-[Hello]: org.apache.openjpa.util.MetaDataException: The id class specified by type "class org.me.mfa.jpa.Registration" does not match the primary key fields of the class. Make sure your identity class has the same primary keys as your persistent type, including pk field types. Mismatched property: "user"
So what now?
JPA does not allow primary key classes to contain relationships, only basic types. JPA 2.0 allows relationships to be apart of the ID, but you would move the relationship to the entity class, and have RegPk contain a deviceId and UserId. The Device-User relationship would then be marked with either #Id or #MapsId("user") depending on if you wanted to use a #PKClass or #EmbeddedId within your entity. See http://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/DerivedIdentifiers for an example using a pk class.
In JPA 1.0, you would use a similar setup, except that your Device-User relationship would be marked read-only (either by specifying the #PrimaryKeyJoinColumn annotation, or by marking the #JoinColumn with insertable=false, updatable=false). You would then need to set the primary key basic mapping value manually, pulling the value from the referenced User entity directly. This of course requires that the id in User already be assigned, which might require additional work if both objects are new.
I would add cascade = CascadeType.ALL to #ManyToOne annotation to forward persist action for User entity.
It will look like:
...
#ManyToOne(optional=true, cascade = CascadeType.ALL)
#JoinColumn(name="USER_ID", referencedColumnName="USER_ID")
public User getUser() {
return user;
}
...
Well, if there is not other error, it could work. I'm little confused with optional=true, cuz it can provide id with deviceId,null but that could be OK also.

EJB inheritance strategy

BIG UPDATE:
Ok, I see my problem is much more complicated than I thought. I have tables like this:
Patient:
id
bloodtype
level (FK to level table)
doctor (FK to doctor table)
person (FK to person table)
Doctor:
id
person (FK)
speciality
level (FK to level)
Paramedic:
id
person (FK)
Person:
id
name
surname
Account:
id
login
pass
Level:
id
account (FK)
name (one of: 'patient' , 'paramedic' , 'doctor')
In entity class I'm using now #Inheritance(strategy= InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType=DiscriminatorType.STRING,name="name") in class Level. To check if someone is for ex. patient I have function:
public boolean ifPatient(String login) {
account = accountfacade.getByLogin(login);
for (Level l : account.getLevelCollection()) {
if (l instanceof Patient) {
return true;
}
}
return false;
}
Now I have situation: I'm logged in as a doctor. I want to add a patient. I have something like this:
public Doctor findDoctor(String login) {
account = accountfacade.getByLogin(login);
for (Doctor d : doctorfacade.findAll()) {
if (d.getLevel().getAccount().getLogin().equals(login)) {
doctor = d;
return doctor;
}
}
}
#Override
public void addPatient(Account acc, Patient pat, Person per, String login) {
Doctor d = findDoctor(login);
accountfacade.create(acc);
personfacade.create(per);
Patient p = new Patient();
p.setAccount(acc);
p.setBlood(pat.getBlood());
// etc
p.setPerson(per);
d.getPatientCollection().add(p);
acc.getLevelCollection().add(p);
}
But it doesn't work. Always totally weird errors like duplicate value of primary key in table Account (but I use TableGenerator...) or NULL value in field Account but for INSERT INTO Doctor (how?! I'm creating new Patient, NOT Doctor...). I'm totally lost now, so I think most important for me now is to know if actually I can use InheritanceType.JOINED in this case.
UPDATE:
You are using the field nazwa as discriminator
#DiscriminatorColumn(discriminatorType=DiscriminatorType.STRING,name="nazwa")
The framework stores there the name of the class that it has to use to deserialize the object (it is the name of the PoziomDostepu class).
As far as I can see you are using a different table for each class, so the Strategy.JOINED would make little sense.
More update:
Where I said class it meant entity. You can check the effect by changing the entity name (say to "CacaCuloPedoPis") of PoziomDostepu and seeing which is the new value being inserted.
Looks like all that is missing is a #DiscriminatorValue annotation on the PoziomDostepu class to use one of the values in your constraint, otherwise it defaults to the class name. The insert of PoziomDostepu is coming from the this.poziom - if you don't want PoziomDostepu instances inserted you might make the class abstract and make sure this instance is a subclass instead. You shouldn't need to change the inheritance type, as doing so will require changing the database tables to each contain the same fields.
Ok, I've done what I wanted. It's not elegant, but efficient. Tables and inheritance strategy are the same. In endpoint I create account and person entities, then I create Patient entity but using EntityManager persist() method. I also need to set id of level manually ((Integer)poziomfacade.getEntityManager().createQuery("SELECT max(p.idpoziom) FROM PoziomDostepu p").getSingleResult())+1 because Generator in Level entity class doesn't work. Nevertheless the problem is solved, thanks for every constructive comment or answer.

How Can I have one parent ENTITY and many types of child ENTITIES in JPA?

I am trying to design my ENTITY CLASSES using JPA annotations.
What I am trying to do is as follows :
A USER table with
id,
***email,
password,
activation_key,
active,
role***
and many TYPES OF USER table with for eg.
STUDENT_TABLE
USER_ID
FirstName
LastName
Company
Address
etc
MENTOR
USER_ID
FirstName
LastNAme
DOB
Department
etc
When USER will register depending o their ROLE they will be sotred in two tables (USER,MENTOR/STUDENT)
When they will log in , the ManagedBean will look into the UESR table to verify the authentication.
I tired to use #OneToOne but It just works with two tables.
I would really appreciate if any1 can help!!
Thanks
If I understand correctly, what you want is an inheritance relationship:
#Entity
#Inheritance(strategy = JOINED)
public abstract class User { ... }
#Entity
public class Student extends User { ... }
#Entity
public class Mentor extends User { ... }
The JOINED strategy tells JPA that the fields in the User class are stored in a table (USER), and that each subclass persists its own field in its own table (STUDENT and MENTOR), with a join column used to refer to the USER table ID.

JPA Error : The entity has no primary key attribute defined

I am using JPA in my application. In one of the table, I have not used primary key (I know its a bad design).
Now the generated entity is as mentioned below :
#Entity
#Table(name="INTI_SCHEME_TOKEN")
public class IntiSchemeToken implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="CREATED_BY")
private String createdBy;
#Temporal( TemporalType.DATE)
#Column(name="CREATED_ON")
private Date createdOn;
#Column(name="SCH_ID")
private BigDecimal schId;
#Column(name="TOKEN_ID")
private BigDecimal tokenId;
public IntiSchemeToken() {
}
public String getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Date getCreatedOn() {
return this.createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
public BigDecimal getSchId() {
return this.schId;
}
public void setSchId(BigDecimal schId) {
this.schId = schId;
}
public BigDecimal getTokenId() {
return this.tokenId;
}
public void setTokenId(BigDecimal tokenId) {
this.tokenId = tokenId;
}
}
Here In my project, eclipse IDE shows ERROR mark(RED colored cross) on this class and the error is "The entity has no primary key attribute defined".
Can anyone tell me, How to create an entity without primary key ?
Thanks.
You can't. An entity MUST have a unique, immutable ID. It doesn't have to be defined as a primary key in the database, but the field or set of fields must uniquely identify the row, and its value may not change.
So, if one field in your entity, or one set of fields in your entity, satisfies these criteria, make it (or them) the ID of the entity. For example, if there is no way that a user can create two instances in the same day, you could make [createdOn, createdBy] the ID of the entity.
Of course this is a bad solution, and you should really change your schema and add an autogenerated, single-column ID in the entity.
If your Primary Key(PK) is a managed super class which is inherited in an entity class then you will have to include the mapped super class name in the persistence.xml file.
Look at the bug report:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=361042
If you need to define a class without primary key, then you should mark that class as an Embeddable class. Otherwise you should give the primary key for all entities you are defining.
You can turn off (change) validation that was added.
Go to workspace preferences 'Java Persistence->JPA->Errors/Warnings' next 'Type' and change 'Entity has no primary key' to 'Warnning'.
In addition to http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#No_Primary_Key you can use some build-in columns like ROWID in Oracle:
Oracle legacy table without good PK: How to Hibernate?
but with care:
http://www.orafaq.com/wiki/ROWID
Entity frameworks doesn't work for all kind of data (like statistical data which was used for analysis not for querying).
Another solution without Hibernate
If
- you don't have PK on the table
- there is a logical combination of columns that could be PK (not necessary if you can use some kind of rowid)
-- but some of the columns are NULLable so you really can't create PK because of DB limitation
- and you can't modify the table structure (would break insert/select statements with no explicitly listed columns at legacy code)
then you can try the following trick
- create view at database with virtual column that has value of concatenated logical key columns ('A='||a||'B='||'C='c..) or rowid
- create your JPA entity class by this view
- mark the virtual column with #Id annotation
That's it. Update/delete data operations are also possible (not insert) but I wouldn't use them if the virtual key column is not made of rowid (to avoid full scan searches by the DB table)
P.S. The same idea is partly described at the linked question.
You need to create primary key ,If not found any eligible field then create auto increment Id.
CREATE TABLE fin_home_loan (
ID int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (ID));
Just add fake id field.
In Postgres:
#Id
#Column(name="ctid")
String id;
In Oracle:
#Id
#Column(name="ROWID")
String rowid;