Merge with JPA and subclass ID - jpa

I got two entities like this (second one have a relation with first one) :
#Entity
#Table(name="FOA_ADRESSE_ICX")
public class FoaAdresseIcx implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="ID_ADRESSE", unique=true, nullable=false, precision=5)
private long idAdresse;
#Column(length=32)
private String bat;
#Column(name="COD_POSTAL", length=5)
private String codPostal;
// getters and setters ....
}
#Entity
#Table(name="FOA_INFOS_ICX")
public class FoaInfosIcx implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="COD_ICX", unique=true, nullable=false, length=8)
private String codIcx;
#Column(name="DATE_RAFFRAICHISEMENT")
#Temporal(TemporalType.TIMESTAMP)
private Date dateRaffraichisement;
#Column(name="LIB_AGENCE", nullable=false, length=98)
private String libAgence;
//uni-directional many-to-one association to FoaAdresseIcx
#ManyToOne
#JoinColumn(name="ID_ADRESSE", nullable=false)
private FoaAdresseIcx foaAdresseIcx;
// getters and setters....
}
I got a problem with the merge :
myEntityMgr.merge(myFoaInfosIcx);
Got this exception :
GRAVE: EJB Exception: : javax.persistence.EntityNotFoundException: Unable to find com.groupama.middlgan.entities.FoaAdresseIcx with id 0
In myFoaInfosIcx id is 0 because I don't initialise it, because I want JPA to create new FoaAdresseIcx in database if doesn't exist.
How can I do that ?

Using primitive types as DB ids has the downside you are currently experiencing. The default value is 0, which is a perfectly valid value for a DB id.
The persistence provider assumes that the entity is not new, but detached and behaves accordingly.
A solution is to use wrapper classes (or other non-primitive classes like UUID) as ids - Long in you case. Unless explicitly instantiated, the id attributes will be null and the provider will correctly identify an entity as new.

Related

The attribute [] is not present in the managed type

I'm doing an application that has this relation ship: A personal contact has an Email.
So i'm trying to find the Emails from the personal contact and I'm doing this query using Criteria but always return IllegalArgumentException:
#Override
public Email findByEmail(PersonalContact personalContact) {
CriteriaBuilder criteriaBuilder = entityManager().getCriteriaBuilder();
CriteriaQuery<Email> criteriaQuery = criteriaBuilder.createQuery(Email.class);
Root<Email> email = criteriaQuery.from(Email.class);
criteriaQuery.where(criteriaBuilder.equal(
email.get("personalContact"), criteriaBuilder.parameter(PersonalContact.class, "personalContact")));
TypedQuery<Email> typedQuery = entityManager().createQuery(criteriaQuery);
typedQuery.setParameter("personalContact", personalContact);
return typedQuery.getSingleResult();
}
Personal contact is like a foreign key.
And here is my Email class:
#Entity
#Table(name = "Email")
public class Email implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String mainEmail;
private List<String> secondaryMail;
#JoinColumn(name = "personal")
#OneToOne(fetch = FetchType.LAZY)
private PersonalContact pContact;
and here is my Personal Contact class:
#Entity
#Table(name = "PERSONALCONTACT")
public class PersonalContact extends Contact implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PERSONAL_ID")
private Long id;
//Other variables
#OneToOne(fetch=FetchType.LAZY, mappedBy="personal")
private Email email;
And every time I execute the query this is the return:
Exception in thread "AWT-EventQueue-0"
java.lang.IllegalArgumentException: The attribute [personalContact] is
not present in the managed type [EntityTypeImpl#1230307250:Email [
javaType: class csheets.ext.crm.contact.Email descriptor:
RelationalDescriptor(csheets.ext.crm.contact.Email -->
[DatabaseTable(Email)]), mappings: 5]].
I did some search and the others programmers said the problem was on the name of the variables... but i guess the names of the variables are correct.
So what I'm doing wrong? perhaps the relationship between that two classes?
Thank you!
If you read the exception message carefully, you'll find that it is complaining that class Email does not have a property (attribute) called personalContact, and indeed, there is no such property. Presumably you meant the pContact property?
(Mistakes such as this are why I recommend querying JPA via Querydsl: code completion would likely have prevented this mistake, and even if not, you would have gotten a clear compiler message when trying to use a non-existing property)

Join Table and Spring Data Repository

This is my sample schema and I have generated jpa entities in eclipse.
I am using spring jpa repositories. I want to know if I need to create repository interface for student course table.
I am having doubt over addStudentCourse method of both student and course entity classes. List studentCourses will be always null for new entity, how can I fill student course table while registering student information in system i.e save method on studentRepository.
Student.java
#Entity
#NamedQuery(name="Student.findAll", query="SELECT s FROM Student s")
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long studentid;
private String studentname;
//bi-directional many-to-one association to StudentCourse
#OneToMany(mappedBy="student")
private List<StudentCourse> studentCourses;
........
public StudentCourse addStudentCourse(StudentCourse studentCourse) {
getStudentCourses().add(studentCourse);
studentCourse.setStudent(this);
return studentCourse;
}
public StudentCourse removeStudentCourse(StudentCourse studentCourse) {
getStudentCourses().remove(studentCourse);
studentCours.setStudent(null);
return studentCourse;
}
Course.java
#Entity
#NamedQuery(name="Course.findAll", query="SELECT c FROM Course c")
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long courseid;
private String coursename;
//bi-directional many-to-one association to StudentCourse
#OneToMany(mappedBy="course")
private List<StudentCourse> studentCourses;
public StudentCourse addStudentCourse(StudentCourse studentCourse) {
getStudentCourses().add(studentCourse);
studentCourse.setCourse(this);
return studentCourse;
}
public StudentCourse removeStudentCourse(StudentCourse studentCourse) {
getStudentCourses().remove(studentCourse);
studentCourse.setCourse(null);
return studentCourse;
}
StudentCourse.java
#Entity
#Table(name="STUDENT_COURSE")
#NamedQuery(name="StudentCourse.findAll", query="SELECT s FROM StudentCourse s")
public class StudentCourse implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private StudentCoursePK id;
private String status;
//bi-directional many-to-one association to Course
#ManyToOne
#JoinColumn(name="COURSEID")
private Course course;
//bi-directional many-to-one association to Student
#ManyToOne
#JoinColumn(name="STUDENTID")
private Student student;
...
}
StudentCoursePK.java
#Embeddable
public class StudentCoursePK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(insertable=false, updatable=false)
private long studentid;
#Column(insertable=false, updatable=false)
private long courseid;
...
}
If I understood your question correctly what you want to do is to be able to save a student from the save method in StudentRepository, and that this inserts/updates the student and also inserts/updates the join table.
Since the Student entity is not the owning side (it's mapped by "student" in StudentCourse), saving a Student will not trigger a save on StudentCourse. To do so you can add a cascade property the list for insert, update... or just for everything:
#OneToMany(mappedBy="student", cascade = CascadeType.ALL)
private List<StudentCourse> studentCourses = new ArrayList<StudentCourse>();
Then you could a method on your #Service class that looks like this:
#Transactional
public void enrollInCourse(Student student, Course course) {
StudentCourse sc = new StudentCourse();
sc.setStudent(student);
sc.setCourse(course);
sc.setStatus("Enrolled");
student.getStudentCourses().add(sc);
studentRepository.save(student);
}
This will also populate the StudentCourse table.
So there's no need for a repository, although if the cascade doesn't work as expected you could create one and save the StudentCourse entity yourself manually.
If this does not work you could try changing your mappings. For n-ary relationships or join tables with extra columns I always define the #ManytoOne relationships inside the #Embeddable class, and in the entity that represents the join table I define getters as #Transient to allow access to the mapped objects which are inside the embedded composite Id.
You can see an example here, and a blog post about this approach here.

How to map existing JPA entities to PicketLink

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.

JPA Exception- Can not find constructor for <Class> with argument types "[class java.lang.String, class java.lang.String]" to fill data

I use jBoss Fuse 6.1.0 with blueprint DSL with openJPA. I use Container Managed transaction (JTA) and transaction managed by Aspects that handles Commit and Rollback as of now
I have following Classes that are JPA entities.
#Entity
#Table(name="CLIENT")
#NamedQuery(name="Client.findAll", query="SELECT c FROM Client c")
public class Client implements Serializable {
private static final long serialVersionUID = 1L;
//Had to add this for avoiding exception. And it works as expected
//Dummy constructor for JPA - Workaround
public Client(String s1, String s2){}
#Column(name="requestid", unique=true,nullable=false)
private String requestId;
#Id
#Column(name="clientid", unique=true, nullable=false, length=128)
private String clientId;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="REQUESTID", nullable=false)
private RoccoRequest roccoRequest;
//bi-directional One-To-Many association to ClientGroup
#OneToMany(mappedBy="client",fetch=FetchType.LAZY)
private List<ClientGroup> clientGroups;
....
,...
...
}
#Entity
#Embeddable
#Table(name="CLIENTGROUP")
#NamedQuery(name="ClientGroup.findAll", query="SELECT c FROM ClientGroup c")
public class ClientGroup implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ClientGroupPK id;
#Column(length=32)
private String type;
#Column(name="clientid", length=128)
private String clientId;
//bi-directional many-to-one association to Client
#ManyToOne(fetch=FetchType.EAGER)
#MapsId("clientid")
#JoinColumn(name="CLIENTID", nullable=true, insertable=false, updatable=false)
private Client client;
..
.
.
.
}
#Entity
#Table(name="ROCCOREQUEST")
#NamedQuery(name="RoccoRequest.CHECK_EXISISTING_CLIENT_DETAILS",
query="SELECT r FROM RoccoRequest r JOIN r.client c WHERE c.crmId = :crmId")
public class RoccoRequest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="requestid", unique=true, nullable=false, length=128)
private String requestId;
#OneToOne(mappedBy="roccoRequest", fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.REMOVE})
private Client client;
..
..
..
CriteriaQuery<Client> criteriaQuery = criteriaBuilder.createQuery(Client.class);
Root<Client> clientRoot = criteriaQuery.from(Client.class);
//Join the Client table with the RoccoRequest table
final Join<Client, RoccoRequest> clientRoccoJoin = clientRoot.join(Client_.roccoRequest,JoinType.INNER);
final Path<String> _requestStatus = clientRoccoJoin.get(RoccoRequest_.statusCode);
final Path<String> _requestId = clientRoccoJoin.get(RoccoRequest_.requestId);
final Predicate _crmIdPredicate = criteriaBuilder.equal(clientRoot.get(Client_.crmId), CRMId);
criteriaQuery.multiselect(_requestId,_requestStatus);
criteriaQuery.where(_crmIdPredicate);
//Get list of details of existing requests for the client with the request type as ACO
clientDetails = entityManager.createQuery(criteriaQuery).getResultList();
if(null != clientDetails) for(Client clientDetail : clientDetails){
StatusBO statusDetails = new StatusBO();
statusDetails.setCode((clientDetail.getRoccoRequest().getStatusCode()));
PreInitiationBO preinitiateDetails = new PreInitiationBO();
preinitiateDetails.getCaseHeader().setRequestId(requestId);
preinitiateDetails.setStatus(statusDetails);
exisitngRequestInfo.add(preinitiateDetails);
}
I have did some Criteria fetching of the entities. But I'm getting an exception as follows:
Can not find constructor for "class com.xxx.xxx.model.Client" with
argument types "[class java.lang.String, class java.lang.String]" to
fill data.
Why does JPA expect an argument Constructor? It has anything to do with the association? I tried removing the OneToMany relationship but I still get the error.
Please note that I have added a 2 argument constructor that makes no sense to me. But it works if it's given. log root level has Debug enabled. It has very less information on exception.
Please help.
As JBNizet pointed out,
I was making a dumb mistake by adding multiselect with two Strings but was having a CrtieriaQuery of type Client.class.
This can either be solved by removing the multiselect(Not in my case) or by Making the CriteriaQuery and other types with Tuples.class instead of Client.class and loop through the Tuples and get as tuple.get(0) etc.
Problem resolved. Thanks #Neil and #JBNizet

Multiple writable mappings exception in EclipseLink

I have these tables:
Which my intention is : A user can be a company or a person but each one of them have something in common, as username which is the email and password, so I used the JPA Tools to generate the entities from the table which result on this:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String email;
private String password;
private int reputation;
//bi-directional one-to-one association to Company
#OneToOne(mappedBy="user", cascade={CascadeType.ALL})
private Company company;
//bi-directional many-to-one association to Location
#OneToMany(mappedBy="user")
private List<Location> locations;
//bi-directional one-to-one association to Person
#OneToOne(mappedBy="user")
private Person person;
//bi-directional many-to-one association to Product
#OneToMany(mappedBy="user")
private List<Product> products;
//bi-directional many-to-one association to UserType
#ManyToOne
#JoinColumn(name="type")
private UserType userType;
//bi-directional many-to-one association to UserPhone
#OneToMany(mappedBy="user")
private List<UserPhone> userPhones;
//bi-directional many-to-one association to UserPicture
#OneToMany(mappedBy="user")
private List<UserPicture> userPictures;
//bi-directional many-to-one association to UserSocialNetwork
#OneToMany(mappedBy="user")
private List<UserSocialNetwork> userSocialNetworks;
// getter and setters
}
Now if I try to persist an user object launchs the follow exception in EclipseLink:
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [COMPANY.id_user]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.OneToOneMapping[user]
Descriptor: RelationalDescriptor(entity.Company --> [DatabaseTable(COMPANY)])
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [PERSON.id_user]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.OneToOneMapping[user]
Descriptor: RelationalDescriptor(entity.Person --> [DatabaseTable(PERSON)])
Runtime Exceptions:
Is the generated mapping wrong ?
How can I solve this exception ?
Update
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id_user")
private int idUser;
private String email;
private String name;
//bi-directional many-to-one association to Area
#ManyToOne
#JoinColumn(name="area")
private Area areaBean;
//bi-directional one-to-one association to User
#OneToOne(cascade={CascadeType.ALL})
#JoinColumn(name="id_user", insertable=false, updatable=false)
private User user;
// getter and setters
}
#Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id_user")
private int idUser;
#Temporal( TemporalType.DATE)
private Date birthdate;
private String gender;
private String name;
private String surname;
//bi-directional one-to-one association to User
#OneToOne
#JoinColumn(name="id_user", insertable=false, updatable=false)
private User user;
// getters and setters
}
I solved my problem placing the insertable=false, updatable=false in the #JoinColumn annotation in both classes, Person and Company.
The proper way to do this is to use #PrimaryKeyJoinColumn instead of plain old #JoinColumn.
reference wiki example on PrimaryKeyJoinColumn
My guess is that you have the id_user mapped twice, once using a Basic #Id mapping, and once using the #ManyToOne. You need to make one of them read-only, i.e. insertable/updatable=false. Or better, just remove the basic id, and put the #Id on the #ManyToOne.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Primary_Keys_through_OneToOne_and_ManyToOne_Relationships