JPA find from composite Key - jpa

I have a class like this...
#Entity
public class User{
private String userId;
#Id
public String getUserId(){
return userId;
}
public void setUserId(String userId){
this.userId = userId;
}
}
#Embeddible
public class RegPk{
private String serial;
private String userId;
....
}
#Entity
#IdClass(RegPk.class)
public class Registration {
private String userId, serial;
private User user
#Id
#Column(name="SRL_C")
public String getSerial() {return serial;}
public void setSerial(String serial) {this.serial = serial;}
#ManyToOne(cascade={CascadeType.REFRESH})
#JoinColumn(name="USERID", referencedColumnName="USERID", nullable = false)
public User getUser() {return user;}
public void setUser(User user) {this.user = user;}
#Id
#Column(name="USERID", nullable = false)
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
RegPk pk = new RegPk();
pk.setSerial(dr.getSerial());
pk.setUserId(dr.getUserId());
Registration userOld = em.find(Registration.class, pk);
But when I try to run it I get null back. I swear I thought I had it working so...
1.) is this kind of thing even possible?
2.) what am I doing wrong?

Yes, it's possible, provided you use the MapsId annotation. Otherwise, you have two different fields mapped to the same column, which is invalid.
The javadoc provides an example which almost matches exactly with your situation.

Related

POST REST request including a foreign key OnToMany Mapping

i'm new to Springboot. I'm trying to implement a simple REST api using :
-Springboot, JPA & rest along with hibernate
I have a 2 tables database, Notebook that contains 1 to many notes
I already setup the 2 tables and relationships. I also created a NotebookRepository and NoteRepository to get basic CRUD operations via the springboot rest. The Database connection and relationships are functionning
but i don't know how to add a new note (it has a notebook_id foreign key which msut NOT be NULL) and everytime i tryto post something along these lines
{
"title:"abc",
"text":"whatever",
"notebook":{
"id":2
}
}
i get this error :
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'notebook_id' cannot be null
#Entity
#Table(name="notebook")
public class NoteBook {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#OneToMany(mappedBy="notebook", cascade=CascadeType.ALL)
List<Note> notes;
public NoteBook() {
}
public NoteBook(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Note> getNotes() {
return notes;
}
public void setNotes(List<Note> notes) {
this.notes = notes;
}
public void addNote(Note note) {
if(notes == null) {
notes = new ArrayList<>();
}
note.setNotebook(this);
notes.add(note);
}
#Entity
#Table(name="note")
public class Note {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="title")
private String title;
#Column(name="text")
private String text;
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.DETACH, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="notebook_id")
private NoteBook notebook;
public Note() {
}
public Note(String title, String text) {
this.title = title;
this.text = text;
}
#RepositoryRestResource(collectionResourceRel = "note", path = "notes")
public interface NoteRepository extends JpaRepository<Note, Integer>{
//No code...
}
#RepositoryRestResource(collectionResourceRel = "notebook", path = "notebooks")
public interface NotebookRepository extends JpaRepository<NoteBook, Integer>{
}
The problem is that the class Note doesn't have a constructor with NoteBook parameter to pass the created NoteBook object to, so the solution is to add this constructor:
public Note(String title, String text, NoteBook noteBook) {
this.title = title;
this.text = text;
this.noteBook = noteBook;
}
and it's enough to send the JSON object as you do, but just be aware of case-sensitivity:
{ "title:"abc", "text":"whatever", "noteBook":{ "id":2 } }
I think you need to add referencedColumnName = "id" for JoinColumn annotation for notebook field in Note class.
Maybe you have problem with IDENTITY generation type. See this problem with null pointer

spring data not get id annotaion on method

When my Entity id annotation on field, it's OK on spring data jpa.
public class User {
#Id
#Column(name="id")
Long rid;
#Column(name="name")
String name;
}
but not work on method...
public class User {
Long rid;
String name;
#Id
#Column(name="id")
public Long getId() {
return this.id;
}
#Column(name="name")
public Long getName() {
return this.name;
}
}
the error message is PersistentEntity does not have an identifier property!
why?
I need to set annotation on method, because I want to set association auto.
#OneToMany(mappedBy="user")
#Cascade(CascadeType.ALL)
public Set<Phone> getPhones() {
return smtpRecords;
}
public void setPhones(Set<Phone> phones) {
phones.forEach(e -> e.setUser(this));
this.phones= phones;
}
any solution? Thanks.

Creating JPA entity with composite primary key with #Id from #MappedSuperclass

I have a class hierarchy for JPA entities with the base class being a MappedSuperclass which has one ID defined. I am trying to use a composite key in a subclass however that does not seem to work
My code looks like this
#MappedSuperclass
public class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
#Entity
#EntityListeners(EntityBaseListener.class)
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name = "catalog_entity")
public class BaseCatalogEntity extends BaseEntity {
#Column(name = "created_at", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
#Column(name = "updated_at", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
public void setCreatedAt(Date date)
{
createdAt = date;
}
public void setUpdatedAt(Date date)
{
updatedAt = date;
}
public Date getCreatedAt() {
return createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
}
#Entity
#Table(schema = "student_catalog")
#IdClass(value = StudentCatalog.StudentCatalogPK.class)
public class StudentCatalog extends BaseCatalogEntity {
#Id
#Column(name = "name", nullable = false, length = 100)
private String name;
#Id
#Column(name = "version", nullable = false)
private Integer version;
#Column(name = "description" , length = 255)
private String description;
#Column(name = "vendor" , length = 50)
private String vendor;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVendor() {
return vendor;
}
public void setVendor(String vendor) {
this.vendor = vendor;
}
public static class StudentCatalogPK implements Serializable {
private Long id;
private String name;
private Integer version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
#Override
public boolean equals(Object obj) {
boolean result = false;
if(obj != null && (obj instanceof StudentCatalogPK)) {
StudentCatalogPK other = (StudentCatalogPK)obj;
result = (Objects.equals(this.id, other.id) && Objects.equals(this.name, other.name) &&
Objects.equals(this.version, other.version));
}
return result;
}
#Override
public int hashCode() {
return (27780 + (this.id != null ? this.id.hashCode() : 0) +
(this.version != null ? this.version.hashCode() : 0) +
(this.name != null ? this.name.hashCode() : 0));
}
}
}
I get the following exception:
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [com.example.jpa.StudentCatalog$StudentCatalogPK] and those of the entity bean class [class com.example.jpa.StudentCatalog] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an #Id on the corresponding fields or properties of the entity class.
I am using Eclipselink 2.5.1. Is there a way I can get this to work without changing the BaseEntity and BaseCatalogEntity classes?
It is not legal in JPA to redefine the id in subclasses. This would lead to ambiguities in the table mappings as well as in polymorphic queries.
The desire to extend the key defined in a superclass is a common issue when business keys are used for DB identity. I would advise to use only surrogate keys (like UUID) for DB identity and business keys for instance identity.
Under following conditions:
your base entity should use TABLE_PER_CLASS inheritance (and as I can see it is)
your base entity (composite key) key is of the same type as that one you want to have in your derived class (so there should be also composite key of String and Integer).
You can use #AttributeOverride annotation under class declaration, removing #Id fields from it:
#AttributeOverride(name = "id", column = #Column(name = "NAME"))
This - in result, can change column name in derived entity's table and that's the most you can acheive.
When using #MappedSuperClass, it would be advisable to make the BaseEntity Class as abstract and then extending the Base class from other Entity classes.
Cleaner approach keeping inheritance in mind and designing your application.

"DataNucleus Enhancer completed with an error"

Am trying to create a model class to store an Entity in Google App Engine using Eclipse.But when i save my work i get the error message:
org.datanucleus.metadata.MetaDataManager initialiseFileMetaDataForUse.
SEVERE: Class "com.packagename.classname" has been specified with an object-id class javax.jdo.identity.StringIdentity yet no fields have been identified as primary key fields. Please notate using the "primary-key" tag against the fields that should be considered part of the primary key.
If my understanding of JPA is correct, i do not need a primary-key for an entity since i already have a #Id tag.Here is my class.
#Entity
public class MyCLassName {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private static String userName;
private static String location;
private static Date dateOfBirth;
private static int age;
private static String gender;
private static String teamName;
private static int weight;
//Constructor with arguments
public MyClassName(String userName, String location, String gender, int age, Date DOB, int weight) {
MyClassName.userName = userName;
MyClassName.location = location;
MyClassName.gender = gender;
MyClassName.age=age;
MyClassName = DOB;
MyClassName.weight = weight;
}
//setter methods
public static void setUserName(String userName) {
MyClassName.userName = userName;
}
public static void setLocation(String location) {
MyClassName.location = location;
}
public static void setGender(String gender) {
MyClassName.gender = gender;
}
public static void setAge(int age) {
MyClassName.age = age;
}
public static void setDateOfBirth(Date dateOfBirth) {
MyClassName.dateOfBirth = dateOfBirth;
}
public static void setWeight(int weight) {
MyClassName.weight = weight;
}
//getter methods
public static String getUserName() {
return userName;
}
public static int getWeight() {
return weight;
}
public static String getLocation() {
return location;
}
public static String getGender() {
return gender;
}
public static String getTeamName() {
return teamName;
}
public static Date getDateOfBirth() {
return dateOfBirth;
}
public static int getAge() {
return age;
}
}
What exactly am i doing wrong here?
Your understanding of JPA is partially correct. You do not need to assign the primary key because you have used the #Id and #GeneratedValue annotation. The JPA implementation will automatically generate the primary key value as a long integer. However it still needs a field in which to store this ID value. It is trying to do that in userName. See Java Tutorial ID Generation Type: IDENTITY for example.

Copy Entity ID at persist time

I want to copy the entity's UUID, generated at run time to another field.
The entity id is generated via the code described bellow:
package eclipselink.example;
public class UUIDSequence extends Sequence implements SessionCustomizer {
public UUIDSequence() {
super();
}
public UUIDSequence(String name) {
super(name);
}
#Override
public Object getGeneratedValue(Accessor accessor,
AbstractSession writeSession, String seqName) {
return UUID.randomUUID().toString().toUpperCase();
}
...
public void customize(Session session) throws Exception {
UUIDSequence sequence = new UUIDSequence("system-uuid");
session.getLogin().addSequence(sequence);
}
}
Persitence.xml:
property name="eclipselink.session.customizer" value="eclipselink.example.UUIDSequence"
The entity:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
}
How can I instruct JPA (Eclipse-link) to copy the UUID generated at runtime to idCopy field as well?
I'm not 100% sure this will work (I don't know if EclipseLink calls the setter or assigns the field directly), but give this a try:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
this.idCopy = id;
// or
// this.setIdCopy(id);
}
}