I want to create an #Index using hibernate. The index should consist of 3 fields, where some of them are nested within other entities. How can I achieve this?
#Table(name = "my_entity", indexes = {
#Index(name = "my_index", columnList = "id, person.firstname, person.lastname")
})
#Entity
public class MyEntity {
private Long id;
#ManyToOne
private Person person;
}
#Entity
public class Person {
#Id private Long id;
private String firstname;
private String lastname;
}
Result:
Caused by: org.hibernate.AnnotationException: Unable to create unique key constraint (id, person.firstname, person.lastname) on table my_entity: database column 'person.firstname', 'person.lastname' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1684)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1459)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1846)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
... 46 more
columnList takes an array of column names. Not an array of entity properties.
So you just need to provide the names of the columns in the test table, as you would do if you defined the index using SQL.
Documentation
What you try to do here is to create an index over 2 different tables (MyEntity and Person), thus it's impossible.
If you want to do that you have to embed the Person class like this :
#Table(name = "test", indexes = {
#Index(name = "my_index", columnList = "id, person.firstname, person.lastname")
})
#Entity
public class MyEntity {
private Long id;
private Person person;
}
#Embeddable
public class Person {
private String firstname;
private String lastname;
}
Note that I've remplaced #Entity by #Embeddable on the Person class, this way in the database you'll have one table named Test with the columns id, firstname and lastname
Related
I think I have a bad setup for my hibernate database. I have Citizen entities who have one to many relationships with WeeklyCare entities. Below is the relevant code.
Citizen:
#Entity
#Table(name = "citizens")
public class Citizen {
#Id
#Size(max = 10, min = 10, message = "CPR must be exactly 10 characters")
private String cpr;
#OneToMany()
#JoinColumn(name = "cpr")
private List<WeeklyCare> weeklyCare;
}
WeeklyCare:
#Entity
public class WeeklyCare {
#EmbeddedId
private WeeklyCareIdentifier weeklyCareIdentifier;
}
WeeklyCareIdentifier:
#Embeddable
public class WeeklyCareIdentifier implements Serializable {
#NotNull
#Size(max = 10, min = 10, message = "CPR must tbe exactly 10 characters")
private String cpr;
#NotNull
private Integer week;
#NotNull
private Integer year;
}
I have some problems when I want to save data to the database:
I can't save WeeklyCare first, because it requires a Citizen.
When I send the citizens to my backend, the objects contain a list of WeeklyCare. When I try to save the citizens, it gives me this error: Unable to find Application.Models.WeeklyCare with id Application.Models.WeeklyCareIdentifier#b23ef67b
I can solve the problem by clearing the list of WeeklyCare on the Citizen before saving it, and then saving the list of WeeklyCare after, but that feels like a terrible way to do it.
I guess I want hibernate to ignore the list of WeeklyCare when it saves a Citizen, but acknowledge it when it fetches a Citizen. Is this possible? Or is there an even better way to do it? Thanks.
I can't save WeeklyCare first, because it requires a Citizen.
You have the "cpr" identifier used in two entities:
it's the primary Id for Citizen
it's part of the composite Id for WeeklyCare
You could, theoretically speaking, create a list of WeeklyCare (not with the way it is modeled now though) and later update the associations of each WeeklyCare to Citizen.
When I send the citizens to my backend, the objects contain a list of WeeklyCare. When I try to save the citizens, it gives me this
error: Unable to find Application.Models.WeeklyCare with id
Application.Models.WeeklyCareIdentifier#b23ef67b
The best way to map One-To-Many association is bidirectional. This will also save you from some unnecessary queries Hibernate is generating when using #OneToMany with #JoinColumn only.
1) Remove cpr from WeeklyCareIdentifier class (and probably rename the class).
#Embeddable
public class WeeklyCareIdentifier implements Serializable {
#NotNull
private Integer week;
#NotNull
private Integer year;
//constructors, getters, setters
}
2) Remove the composite #EmbeddedId in favor of Long id field:
#Entity
public class WeeklyCare {
#Id
#GeneratedValue
private Long id;
#Embedded
private WeeklyCareIdentifier weeklyCareIdentifier;
//constructors, getters, setters
}
3) Move to bidirectional mapping:
#Entity
#Table(name = "citizens")
public class Citizen {
#Id
#Size(max = 10, min = 10, message = "CPR must be exactly 10 characters")
private String cpr;
#OneToMany(
mappedBy = "citizen",
cascade = CascadeType.ALL, //cascade all operations to children
orphanRemoval = true //remove orphaned WeeklyCare if they don't have associated Citizen
)
private List<WeeklyCare> weeklyCares = new ArrayList<>(); //init collections to avoid nulls
//constructors, getters, setters
//add utility methods to manipulate the relationship
public void addWeeklyCare(WeeklyCare weeklyCare) {
weeklyCares.add(weeklyCare);
weeklyCare.setCitizen(this);
}
public void removeWeeklyCare(WeeklyCare weeklyCare) {
weeklyCares.remove(weeklyCare);
weeklyCare.setCitizen(null);
}
}
and:
#Entity
public class WeeklyCare {
#Id
#GeneratedValue
private Long id;
//having reference to the citizen entity from WeeklyCare
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "citizen_cpr")
private Citizen citizen;
#Embedded
private WeeklyCareIdentifier weeklyCareIdentifier;
//constructors, getters, setters
}
I would also recommend to use Long ids for the entities, even if the cpr is unique and so on. Convert the cpr to a normal column and introduce a DB generated ID column which you use in to join with in your internal domain and treat the cpr as a pure user-facing data column.
I have the following database tables: lookup and employee.
Lookup Table: structure with sample data.
class_name value description
GENDER_CODE 1 Male
GENDER_CODE 2 Female
BANK_CODE 1 HSBC
BANK_CODE 2 CityBank
Employee Table: structure with sample data.
id name gender_code bank_code
1 Yusuf 1 1
2 Maher 1 2
3 Suzan 2 1
What is the best way to map them into JPA entities?
I tried to map an abstract class to lookup table and use class_name column as discriminator for the subclasses Gender and Bank and reference the bank and gender as ManyToOne in the employee object.. but I'm getting a class cast exception when the gender_code and bank_code has the same value.
I tried to create views gender_lookup and Bank_lookup and map them directly to entities. Again hibernate complains that he can't find a table with such name.
I would try to map the lookuptable as n+1 separated entities, one abstract and n childs.
Mapped superclass should have SINGLE_TABLE inheritance and child classes need to declare the discriminator.
Something like this:
#MappedSuperclass
#DiscriminatorColumn(name = "class_name")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class LookupTable{
#Id
private Long vale;
#Column(nullable = false)
private String description;
// Getters and setters
}
#Entity
#DiscriminatorValue("GENDER_CODE")
public class GenderCode extends LookupTable() {
}
#Entity
#DiscriminatorValue("BANK_CODE")
public class BankCode extends LookupTable() {
}
#Entity
public class Employee{
#Id
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private GenderCode genderCode;
#Column(nullable = false)
private BankCode bankCode;
}
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.
I am using Sprind JPA, Spring 3.1.2(in future 3.2.3), Hibernate 4.1 final.
I am new to Sprind Data JPA. I have tow Table Release_date_type and Cache_media which entities are as follows :
ReleaseAirDate.java
#Entity
#Table(name = "Release_date_type")
public class ReleaseDateType {
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private Integer release_date_type_id;
#Column
private Integer sort_order;
#Column
private String description;
#Column
private String data_source_type;
#Column(nullable = true)
private Integer media_Id;
#Column
private String source_system; with getters and setters..
and CacheMedia as
#Entity
#Table(name = "Cache_Media")
public class CacheMedia {
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private Integer id;
#Column(name="code")
private String code;
#Column(name="POSITION")
private Integer position;
#Column(name="DESCRIPTION")
private String media_Description; with setter and getters.
Now my repository interface is as follows :
public interface ReleaseDateTypeRepository extends CrudRepository<ReleaseDateType, Long>{ }
Now i want to write a method(Query) in ReleaseDateTypeRepository interface which can get all the data from Release_Date_Type table including appropriate media_description from Table 'Cache_Media' based on media_id of Release_date_type table.
So my select (SQL)query looks like
SELECT * from Release_Date_Type r left join Cache_Media c on r.media_id=c.id
I don't know how to map entities.
I tried so many thing but not luck.
Any help is appreciated.
Its not the answer for joining via Hibernate, but alternatively you can create a view with your join and map the view to your objects
EDIT
Hi all.
WHAT I TRY TO GET
I want a table that represents a list of personal details, this table is called PERSON and must have this columns:
ID NAME SURNAME STREET ID_CITY
Well, i already have a table in my db that contains all municipalities of my country, this table is called MUNICIPALITY and has this columns:
ID COUNTRY PROVINCE NAME
I wrote the class Municipality.java to reepresents the table above, this class is:
#Entity
public class Municipality implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String country;
private String province;
private String name;
public Municipality() {
}
...
So, i thought a good implementation of PERSON table is to write an emebedded class Address.java that contains some info about "the address" of the person to embed in the Person.java class.
Address.java simpy contains a street and a municipality object:
#Embeddable
public class Address implements Serializable {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_CITY")
#Cascade(CascadeType.SAVE_UPDATE)
private Municipality municipality;
#Column(length=45)
private String street;
public Address() {
}
...
Address is simply emebedded in Person.java class:
#Entity
public class Person implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
private String surname;
#Embedded
private Address address;
public Person() {
}
...
Well, if now i save a Person instance:
Person p=new Person();
Address a=new Address();
a.setStreet("bla bla");
Municipality mun=new Municipality();
mun.setName("ZZZ");
a.setMunicipality(mun);
p.setAddress(a);
session.save(p);
All works fine, hibernate create a table PERSON exactly how i want.
THE PROBLEM
But now, i want also the list of all address associate to a municipality object, so i do not necessary do a "select" for this.
I thougth i good thing is to add a collectin of Address to Municipality:
#CollectionOfElements
private List<Address> addressList;
But now if i save a person, hibernate create a new table MUNICIPALITY_ADDRESSLIST:
MUNICIPALITY_ID ID_CITY STREET
The table above contains always NULL values... so is totally useless, and i don't want it!
I read some docs about #CollectionOfElements and #OneToMany and i find i can define a join table. So i thought to set PERSON like join table...
#CollectionOfElements
#JoinTable(name="PERSON")
private List<Address> addressList;
First insert of Person works good, i obtain the PERSON table how i want, and MUNICIPALITY_ADDRESSLIST is no longer created.
But when i make the second insert, hibernate add a column to PERSON table: MUNICIPALITY_ID
ID STREET NAME SURNAME ID_CITY MUNICIPALITY_ID
MUNICIPALITY_ID contains always NULL values, so why this column is added?
Sure my implementations is very bad, im new to hibernate and JPA, so where is my main mistake?
I hope now is more understandable... sorry for my bad english.
Thanks.
blow, if you do not really want MUNICIPALITY_ADDRESSLIST Table, you should use #Transient instead of #CollectionOfElements
public class Municipality {
#Transient
private List<Address> addressList;
}
If you use #CollectionOfElements, Hibernate will always create MUNICIPALITY_ADDRESSLIST Table. If #Transient does not fullfil your needs, provide more info about what you want to get
UPDATE
According to info provided by yourself
Person has an #Embedded Address which has a #ManyToOne relationship with Municipality
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Embedded
private Address address;
}
Now, our Address
/*
* #Embeddable class MUST implements Serializable
*/
#Embeddable
public class Address implements Serializable {
#ManyToOne
/*
* If you are using Hibernate INSTEAD OF JPA
* Prefer to use #Cascade Hibernate annotation, as shown bellow,
* instead of JPA cascade Attribute
*
* Also notice it can not be null - see nullable Attribute
*/
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name="MUNICIPALITY_ID", nullable=false)
private Municipality municipality;
}
And Municipality (without ANY List of Address or Person)
public class Municipality {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
}
As shown above, your DB structure should looks like
PERSON
ID
MUNICIPALITY_ID
MUNICIPALITY
ID
But whether Municipality has a List of Person as follows
public class Municipality {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#OneToMany
private List<Person> personList;
}
You will get The same structure shown above
But you have a bi-directional relationship Person can see Municipality (Through Address class) and Municipality can see Person (Through List of Person)
Now let's see The following scenario
Person person = new Person();
Municipality municipality = new Municipality();
Address address = new Address();
address.setMunicipality(person);
person.setAddress(address);
session.save(person);
You will see
INSERT INTO PERSON blah, blah, blah...
Because of #Cascade(SAVE_UPDATE)
INSERT INTO MUNICIPALITY blah, blah, blah...
But because of The bi-directional relationship, You should set up which property is The owner of The relationship. Owner of The relationship ? What is that ?
Suppose
public class A {
#ManyToOne
private B b;
}
And
public class B {
#OneToMany
#JoinColumn(name="B_ID")
#Cascade(SAVE_UPDATE)
private List<A> aList;
}
And you do
/*
* a.getB() is null, right ?
A a = new A();
B b = new B();
b.getAList().add(a);
SQL outputs
/* Let's suppose B_ID generated value is 5
INSERT INTO B
/*
* because of #Cascade(SAVE_UPDATE)
INSERT INTO A (B_ID) VALUES(5)
But because a.getB() is null
You will also see
UPDATE A SET B_ID = null
It explains why you should use mappedBy attribute when using a bi-directional relationship.
I'm not sure what the expected result is exactly (I'm too lazy to reproduce the case) but you should be able to derive the following sample:
#org.hibernate.annotations.CollectionOfElements(targetElement = java.lang.String.class)
#JoinTable( name="PHONE",joinColumns = #JoinColumn(name="STUDENT_ID"))
#Column(name="PHONE_NO")
public Set<String> getPhones() {
Finally i think to find a solution... i do this:
#Entity
public class Municipality implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String country;
private String province;
private String name;
#OneToMany(mappedBy="address.municipality")
private List<Person> persons;
public Municipality() {
}
...
I have not changed other classes.
Is this correct or work only by chance?
Thanks.