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.
Related
I'm new to Stackoverflow, so I will make my best to conforms with usage. I was wondering if there were a way to get a complete list of changes/snapshots of a given Entity. For now it works well with edition of Singular Properties, as well as Addition and Deletion to Collection Property. But I'm unable to find when a Child Entity in the Collection Property was updated.
Given two Entities, and a LinkEntity:
#Entity
class Person {
#Id
Long id;
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
Set<LinkAddress> addresses;
}
#Entity
class Address {
#Id
Long id;
#OneToMany(mappedBy = "address")
Set<Address> persons;
}
#Entity
class LinkPersonAddress {
#Id
Long id;
#ManyToOne
#ShallowReference
Person person;
#ManyToOne
#ShallowReference
Address address;
String linkType;
}
My use case is following. I get a specific Person by Id #1, and then mutate the type of specific Address (ie. HOME --> WORK). I save the Person back with the modified Set and let JPA Cascade my changes. Although all Spring Data Repositories for Person, Address, and LinkPersonAddress are annotated with #JaversSpringDataAuditable, I cannot retrieve this "update" using Javers QueryBuilder with the class Person and Id #1. It makes sense as I should query the class LinkPersonAddress instead, but how can I specify that I want only the changes from LinkPersonAddress relevant to Person with Id #1.
PS: Please apologize any typos in code snippets, as I didn't write it in my Dev Environment.
Let's start from the mapping. You did it wrong, Address is a classical ValueObject (see https://javers.org/documentation/domain-configuration/#value-object) not Entity. Because:
Address doesn't have its own identity (primary key genereted by a db sequence doesn't count)
Address is owned by the Person Entity. Person with its Addresses forms the Aggregate.
When you correct the mapping, you can use ChildValueObjects filter, see https://javers.org/documentation/jql-examples/#child-value-objects-filter
The EmbeddedId or IdClass annotation is used to denote a composite primary key.
How can i use composite primary key without ( EmbeddedId or IdClass ) ?
If it is possible to use composite primary key without ( EmbeddedId or IdClass ) then how can i use EntityManager.find( Entity Class , Object primaryKey) method to find entity in case of composite primary key(Multiple Primarykey) (because of no IdClass or EmbeddedId) .
EclipseLink take List of pk in the find() operation but if composite pk key defined in example -
Entity Person {
#Id
String username;
#Id
String emailId;
#Basic
String firstName;
#Basic
String lastName;
}
List list = new ArrayList();
list.add(${username}); //Run time value
list.add(${emailId}); //Run time value
then EnitityManager.find(list) will take these arguments , is i am right?
If i am assuming correct then how will EnitityManager.find() operation will know that List 1st argument is username or emailId pk value (means sequence of composite pk fields value)
Let me give my thoughts about it.
find
<T> T find(java.lang.Class<T> entityClass,
java.lang.Object primaryKey)
In order to find an entity of class Person, you should use something like
find(Person.class, Object primaryKey)
As you have a composed key, you should have a IdClass or EmbeddedId like this.
public class PersonKey implements Serializable{
String username;
String emailId;
public PersonKey(String username, String emailId){
//Add lines for correct constructor
}
//Override hascode and equals
}
Then you can find objects based on that key.
find(Person.class, personKey);
your key need to be something like this.
PersonKey personKey = new PersonKey(1,1);
Person p = find(Person.class, personKey);
FIND DON'T ACCEPT LISTS IN PURE JPA !!, accept and return only ONE managed object. If you would use find to retrieve several objects you should call the method N times passed the keys you want to find.
If you use find passing a list you will see something like this.
org.springframework.dao.InvalidDataAccessApiUsageException:
Provided id of the wrong type for class domain.model.Person. Expected:
class domain.key.PersonKey, got class java.util.ArrayList; nested
exception is java.lang.IllegalArgumentException: Provided id of the
wrong type for class
com.staples.sa.pricemart.domain.model.ItemFileEntity. Expected: class
com.staples.sa.pricemart.domain.key.ItemFileKey, got class
java.util.ArrayList
It seems eclipseLink have find that you can do that, but in order to make you application more portable try to use find as is described in JPA.
You should give an entity as the primary key:
Person id = new Person();
id.setUsername("Username");
id.setEmailId("EmailId");
entityManager.find(Person.class, id);
JPA will use the the fields annotated with #Id to find the the record You need.
You should be able to use a List with the find() operation in EclipseLink.
Maybe this is a question with an easy answer ... but I don't get it running. At persist() I get the exception that the referential key in the child table is null (which of course is not allowed by the database). I have a recipe and some steps for preparation.
I'm using EclipseLink 2.4.1
Recipe.java (rcpid is autoset by JPA)
#Entity
public class Recipe {
#Id
long rcpid;
List<Recipestep> recipesteps = new ArrayList<>();
#OneToMany(
cascade=CascadeType.ALL,
fetch=FetchType.EAGER,
mappedBy="recipe",
targetEntity=Recipestep.class )
// This does NOT work. Following line tries to access a join-table !!!
// #JoinColumn(name="rcpid", referencedColumnName="rcpid")
public List<Recipestep> getRecipesteps() { return recipesteps; }
// some more attributes, getters and setters
}
Recipestep.java (rpsid is autoset by JPA)
#Entity
public class Recipestep {
#Id
long rpsid;
Recipe recipe;
#ManyToOne( targetEntity=Recipe.class )
#JoinColumn( name="rcpid" )
public Recipe getRecipe() { return recipe; }
// some more attributes, getters and setters
}
The code above is a valid workaround. However to have clean (and supportable) code, the relationship should be only one-way with a collection in the parent which references all its children.
You have mapped this as a unidirectional one to many, but have two mappings for the recipestep rcpid database column. Try changing the long rcpid to
#ManyTOne
Recipe rcp;
And then remove the joincolumn definition from the oneToMany and make it bidirectional by marking it as mappedby the rcp manyToOne relation. An example is posted here http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Relationship_Mappings/Collection_Mappings/OneToMany
Eclipselink will always insert nulls on unidirectional oneToMany relations using a joincolumn when first inserting the target entity, and then update it later when it processes the Recipe entity. Your rcpid mapping in Recipestep is also likely null, which means you have two write able mappings for the same field which is bad especially when they conflict like this.
You are experiencing the default JPA behaviour. Adding an entity to the recipesteps list is not sufficient to create a bidirectional relation.
To solve the issue you need to set the rcpid explicitly on every element in the list.
EDIT: I think the issue is that JPA does not know where to store the id of the Recipe in the Recipestep table. It assumes a name ("recipebo_rcpid"), but your table seems to lack it.
Try adding the column "recipe_id" to the Recipestep table and a mappedBy attribute to the #OneToMany annotation:
#OneToMany(
cascade=CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy = "recipe" )
You probably do not need the targetEntity attribute in the annotation- the List is typed already.
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;
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