Using Spring Boot with Hibernate JPA
I am having trouble accessing a DAO for an #Entity which has a composite key where one of the columns is a foreign key. It's giving me
org.hibernate.PropertyAccessException: Could not set field value [...] by reflection when I try to do a findOne() using the DAO.
So I have two MySQL relations, all_contacts and contact_phones, represented in order here:
contact_phones has a composite primary key consisting of contactid + number, of those two, contactId is also a foreign key for the same value in all_contacts. I've established the relationship using the proper #OneToMany and #ManyToOne annotations
Entity for all_contacts:
#Entity
#Table(name = "all_contacts")
public class Contact {
#Column(name="userid", columnDefinition ="bigint(13)")
private BigInteger userId;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="contactid", columnDefinition ="bigint(13)")
private BigInteger contactId;
#OneToMany(mappedBy = "contact", cascade = CascadeType.ALL)
#ElementCollection(targetClass=ContactPhones.class)
private Set<ContactPhones> phones = new HashSet<ContactPhones>(0);
// the rest of the fields, including getters and setters
}
Entity for contact_phones:
#Entity
#Table( name ="contact_phones")
#IdClass(ContactPhonesKey.class)
public class ContactPhones {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="contactid", nullable = false)
#Id
private Contact contact;
#Column(name="phone_type", columnDefinition = "")
private String phoneType;
#Id
#Column(columnDefinition ="bigint(13)")
private BigInteger number;
// getters and setters
}
And, because the primary key of the contact_phones class was composite (hence the #IdClass(ContactPhonesKey.class) ), I was forced to create a Key class to direct it:
Class for ContactPhonesKey:
public class ContactPhonesKey implements Serializable {
private Contact contact;
private String number;
public ContactPhonesKey() {}
public ContactPhonesKey(Contact contact, String number) {
this.contact = contact;
this.number = number;
}
// getters and setters
}
However, whenever I try to access something by the DAO (when I have created an instance of it by #Autowired) I made for the contact_phones class:
public interface ContactPhonesRepository extends CrudRepository<ContactPhones, BigInteger> {
List<ContactPhones> findByNumberContaining(String number);
#Query(value ="SELECT * FROM contact_phones cp WHERE cp.contactid= :contactId",
nativeQuery=true)
List<ContactPhones> findAllPhonesByContactId(#Param("contactId")BigInteger contactId);
}
I am getting an error about not being able to set the ContactPhonesKey class due to reflection. Here's the full error I get:
Could not set field value [111456666] value by reflection : [class app.models.relationentities.ContactPhonesKey.number] setter of app.models.relationentities.ContactPhonesKey.number; nested exception is org.hibernate.PropertyAccessException: Could not set field value [111456666] value by reflection : [class app.models.relationentities.ContactPhonesKey.number] setter of app.models.relationentities.ContactPhonesKey.number
There's a type mismatch on the field number between your entity ContactPhones and ID Class ContactPhonesKey. On the entity, it is declared as BigInteger, while on the ID Class, it is declared as String.
Related
im relative new to Hibernate Mappings im trying to achieve this functionality between the class Post and Comentario without luck
Relational model
#Embeddable
public class PostPK implements Serializable {
#Column(name="idPost")
private int postID;
#Column(name="idUsuario")
private int userIDFK;
-------------------------------
#Entity
#Table(name="Post")
public class Post {
#EmbeddedId
private PostPK id;
#ManyToOne
#MapsId(value="userIDFK")
#JoinColumn(name="idUsuario")
private Usuario usuario;
#OneToMany(mappedBy="post")
private List<Comentario> comentarios;
#Column(name="titulo")
private String titulo;
-----------------------------------
#Embeddable
public class ComentarioPK implements Serializable{
#Column(name="idComentario")
private int comentarioId;
#Column(name="idPost")
private int postIdFK;
---------------------------
#Entity
#Table(name="Comentario")
public class Comentario {
#EmbeddedId
private ComentarioPK id;
#ManyToOne
#MapsId("postIdFK")
#JoinColumn(name="idPost",referencedColumnName="idPost")
private Post post;
#Column(name="texto")
private String texto;
without mapping comentario and its fields in Post its working fine but when i decide to map it i get this error
Unable to find column reference in the #MapsId mapping: idUsuario
is it not finding the idUsuario column in Comentario table? i dont want to add it , i can achieve joins in mysql but i dont know how to do it in Hibernate
#MapsId annotation is used to map the primary key fields of the parent entity with the child entity(with the same name).
In your case your are having composite primary key in your parent entity but in child entity you want to refer only one field of it.
PostPK has two fields : idPost and idUsuario. But in Comentario class when your are specifying ManyToOne relationship you are mentioning single column in #JoinColumn(which is idPost) and no field for idUsuario is available in your mapping. But as per the behavior of #MapsId annotation both the fields(idPost and idUsuario) are expected in Comentario class.
Thus, in your case #MapsId annotation won't work
I am using eclipselink 2.5.1.
Let's say I have these two class.
JAVA
#Entity
public class Car implements Serializable {
#EmbeddedId
protected CarPK carPK;
private String color;
#ManyToOne(fetch = FetchType.LAZY)
private Manufacturor manufacturor;
//constructors, getters & setters...
}
#Embeddable
public class CarPK implements Serializable {
#NotNull
private int idManufacturor;
#Temporal(javax.persistence.TemporalType.DATE)
private Date date;
//constructors, getters & setters...
}
Car has a composite primary key (idManufacturor and date) and idManufacturor is also a foreign key referencing the class Manufacturor.
I'm having issue with the mapping. EclipseLink understand the manufacturor object as a column in my Car table.
Error
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: invalid column nameĀ : 'manufacturor'.
I know the problem will be solved if I add a column manufacturor FK but it would be repeating.
Please feel free to ask for any precision if I'm not clear enough.
Thank you for your help.
Add the JoinColumn Annotation
#JoinColumn(name = "id_manufacturor", referencedColumnName = "id")
Name is the FK column name in your database (not entity).
The referencedColumnName "id" must correspond to the defined id in manufacturer table.
I have a (abbreviated) class that looks like this:
#Entity
#Table
#SecondaryTable(
name = "SUPER_ADMIN",
pkJoinColumns = #PrimaryKeyJoinColumn(
name = "PERSON_ID",
referencedColumnName = "PERSON_ID"))
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
// getters/setters omitted for brevity
}
The SUPER_ADMIN table has only one column: PERSON_ID. What I would like to do is add private Boolean superAdmin to Person where it would be true if the PERSON_ID is present in that table.
Is this even possible? I am using Hibernate as my JPA provider, so I'm open to proprietary solutions as well.
UPDATE
It seems like I should have done more homework. After poking around, I see that #SecondaryTable does inner joins and not outer joins. Therefore, my idea here will not work at all. Thanks to #Elbek for the answer -- it led me to this revelation.
You can use JPA callback methods.
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
#Transient
private transient Boolean superAdmin = false;
// This method will be called automatically when object is loaded
#PostLoad
void onPostLoad() {
// BTW, personId has to be present in the table since it is id column. Do you want to check if it is 1?
superAdmin = personId == 1;
}
}
or you can create easy getter method.
public class Person {
#Id
#Column(name = "PERSON_ID")
private Long personId;
boolean isSuperAdmin() {
return personId == 1;
}
}
You can't have an optional relationship with a #SecondaryTable. You do not have any other choice than using a #OneToOne optional relationship in that case.
I have this bean:
#Entity
#Table(name = "accesos")
public class Acceso implements Serializable {
/** */
#Column(name = "idUser")
private String idUser;
/** */
#ManyToOne
#JoinColumn(name = "idArea")
private Area area;
/** */
#ManyToOne
#JoinColumn(name = "idRol")
private Rol rol;
But I get this error:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com...Acceso
How can I set this bean? What I need is based on the user ID get all the ROL-AREA that he has access.
I tried change the #Entity to #Embedded, but when I make the search no result is returned, and even in the log is no SQL sentence executed.
You have to have an identity for each bean, there is no way around. You can however use a combined key, if none of your fields is unique.
If the combination of all your fields is unique, then try to annotate all fields with #Id. Take as few fields as possible, but as many as required to make the combination unique.
JPA Specifications state that all Entities must have an identifier (JSR 317, section 2.4). It can be a single column or a composite key.
You can either put an idAcceso identifier in the Acceso entity or not make Acceso an entity but rather a "component" (which is the purpose of the #Embeddable annotation). Components do not require an ID but cannot be queried separately (i.e. you cannot do select a from Acceso a but rather you need to query for User and then use the accessor method user.getAccesos().
You cannot substitute #Entity with #Embedded in this context.
#Embeddable
public class Acceso {
// ...
}
#Entity
public class User {
#Id protected String id;
// ...
#ElementCollection
#CollectionTable(
name="USER_ACCESSES",
joinColumns=#JoinColumn(name="USER_ID")
protected Set<Acceso> accesos = new HashSet<Acceso>();
}
You don't have an id specified and you MUST so add #Id annotation onto idUser
#Id
#Column(name = "idUser")
private String idUser;
Can anyone tell me whether Hibernate supports associations as the pkey of an entity? I thought that this would be supported but I am having a lot of trouble getting any kind of mapping that represents this to work. In particular, with the straight mapping below:
#Entity
public class EntityBar
{
#Id
#OneToOne(optional = false, mappedBy = "bar")
EntityFoo foo
// other stuff
}
I get an org.hibernate.MappingException: "Could not determine type for: EntityFoo, at table: ENTITY_BAR, for columns: [org.hibernate.mapping.Column(foo)]"
Diving into the code it seems the ID is always considered a Value type; i.e. "anything that is persisted by value, instead of by reference. It is essentially a Hibernate Type, together with zero or more columns." I could make my EntityFoo a value type by declaring it serializable, but I wouldn't expect this would lead to the right outcome either.
I would have thought that Hibernate would consider the type of the column to be integer (or whatever the actual type of the parent's ID is), just like it would with a normal one-to-one link, but this doesn't appear to kick in when I also declare it an ID. Am I going beyond what is possible by trying to combine #OneToOne with #Id? And if so, how could one model this relationship sensibly?
If the goal is to have a shared primary key, what about this (inspired by the sample of Java Persistence With Hibernate and tested on a pet database):
#Entity
public class User {
#Id
#GeneratedValue
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address shippingAddress;
//...
}
This is the "parent" class that get inserted first and gets a generated id. The Address looks like this:
#Entity
public class Address implements Serializable {
#Id #GeneratedValue(generator = "myForeignGenerator")
#org.hibernate.annotations.GenericGenerator(
name = "myForeignGenerator",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "user")
)
#Column(name = "ADDRESS_ID")
private Long id;
#OneToOne(mappedBy="shippingAddress")
#PrimaryKeyJoinColumn
User user;
//...
}
With the above entities, the following seems to behave as expected:
User newUser = new User();
Address shippingAddress = new Address();
newUser.setShippingAddress(shippingAddress);
shippingAddress.setUser(newUser); // Bidirectional
session.save(newUser);
When an Address is saved, the primary key value that gets inserted is the same as the primary key value of the User instance referenced by the user property.
Loading a User or an Address also just works.
Let me know if I missed something.
PS: To strictly answer the question, according to Primary Keys through OneToOne Relationships:
JPA 1.0 does not allow #Id on a OneToOne or ManyToOne, but JPA 2.0 does.
But, the JPA 1.0 compliant version of Hibernate
allows the #Id annotation to be used on a OneToOne or ManyToOne mapping*.
I couldn't get this to work with Hibernate EM 3.4 though (it worked with Hibernate EM 3.5.1, i.e. the JPA 2.0 implementation). Maybe I did something wrong.
Anyway, using a shared primary key seems to provide a valid solution.
Yes that is possible.
Look at the following example using Driver and DriverId class as id for Driver.
#Entity
public class Drivers {
private DriversId id; //The ID which is located in another class
public Drivers() {
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "personId", column = #Column(name = "person_id", nullable = false))})
#NotNull
public DriversId getId() {
return this.id;
}
//rest of class
}
Here we are using personId as the id for Driver
And the DriversId class:
//composite-id class must implement Serializable
#Embeddable
public class DriversId implements java.io.Serializable {
private static final long serialVersionUID = 462977040679573718L;
private int personId;
public DriversId() {
}
public DriversId(int personId) {
this.personId = personId;
}
#Column(name = "person_id", nullable = false)
public int getPersonId() {
return this.personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof DriversId))
return false;
DriversId castOther = (DriversId) other;
return (this.getPersonId() == castOther.getPersonId());
}
public int hashCode() {
int result = 17;
result = 37 * result + this.getPersonId();
return result;
}
}
You can do this by sharing a primary key between EntityFoo and EntityBar:
#Entity
public class EntityBar
{
#Id #OneToOne
#JoinColumn(name = "foo_id")
EntityFoo foo;
// other stuff
}
#Entity
public class EntityFoo
{
#Id #GeneratedValue
Integer id;
// other stuff
}
You have to use #EmbeddedId instead of #Id here.
And EntityFoo should be Embeddable.
Another way is to put an integer, and a OneToOne with updateble and instertable set to false.