Hibernate: #EmbeddedId, Inheritance and #SecondaryTable - java

I'm using Hibernate version 3.3.2.GA with annotations.
I have inheritance between two classes, the former:
#Entity
#Table(name = "SUPER_CLASS")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="DISCR_TYPE",
discriminatorType= DiscriminatorType.STRING
)
#org.hibernate.annotations.Entity(mutable = false)
public class SuperClass { }
The subclass is mapped with a secondary table:
#Entity
#DiscriminatorValue("VALUE")
#org.hibernate.annotations.Entity(mutable = false)
#SecondaryTable(name = "V_SECONDARY_TABLE",
pkJoinColumns = #PrimaryKeyJoinColumn(name = "ID", referencedColumnName = "ID"))
public class SubClass extends SuperClass {
#Embedded
public Field getField() {
return getField;
}
}
Where the field is composed of two different fields
#Embeddable
public class Field {
#Column("FIELD_1") String field1
#Column("FIELD_2") String field2
}
Now when I create a query on SubClass the FIELD_1 and FIELD_2 fields are searched on the SuperClass, even if they're defined in the subclass.
I can't set the table in the #Column annotation in the field, because the Field class it's reused somewhere. I need to specify it in SubClass class.
How do I specify that the field should be searched in the secondary table?
Also on Hibernate Forum

You should use table attribute
#Column("FIELD_1", table="V_SECONDARY_TABLE")
UPDATE
When a embeddable column is used by more than one entity, you should use #AttributeOverride if you need to re-map just a single column or #AttributeOverrides if more than one column
#Entity
#SecondaryTable(name="OTHER_PERSON")
#AttributeOverride(name="address.street", column=#Column(name="STREET", table="OTHER_PERSON"))
public class Person {
private Address address;
#Id
#GeneratedValue
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
#Embedded
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
#Embeddable
public static class Address implements Serializable {
private String address;
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
}
}

Related

Hibernate one-to-many relationship java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null

I'm new to hibernate, learn doc save persistent object
followed hibernate doc this is person and phone relationship one-to-many
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
I'm persistent person and add one phone the error be throw
#Test
public void say() {
Person person = new Person();
person.setUsername("aaaa");
Phone phone = new Phone();
phone.setNumber("111");
person.getPhones().add(phone);
personService.save(person);
}
this is Dao persistent
public class PersonDaoImpl implements PersonDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public void save(Person person) {
entityManager.persist(person);
}
Update service code, service just save person
#Service(value = "personService")
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Transactional
#Override
public void save(Person person) {
personDao.save(person);
}
}
error info:
23:35:47.059 [main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
23:35:47.062 [main] DEBUG org.hibernate.SQL -
insert
into
phone
(number, person_id)
values
(?, ?)
23:35:47.297 [main] DEBUG org.hibernate.engine.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
java.sql.SQLIntegrityConstraintViolationException: Column 'person_id' cannot be null
Add the #GeneratedValue annotation to specify that the primary key for both entities will be populated outside of your code.
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#JoinColumn("person_id")
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Person person;
//omit setter and getter
}
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
//omit getter and setter
}
Additionally, you need to persist the Person object instead of the Phone object because there is no cascade configured from Phone to Person. If you can't do that, switch the CascadeType on Person to none and put the cascade on the Phone as shown above.
You should also add a #JoinColumn annotation on the Phone entity so hibernate is aware of the foreign key column.
You Missed something. You can try with this.
Person Entity
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
#OneToMany(mappedBy = "person")
private List<Phone> phones = new ArrayList<>();
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public List<Phone> getPhones() {
return phones;
}
public void setPhones(List<Phone> phones) {
this.phones = phones;
}
//omit getter and setter
}
Phone Entity
#Entity
#Table(name = "phone")
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "number")
private String number;
#ManyToOne(cascade = CascadeType.PERSIST)
private Person person;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
//ommit setter and getter
}
Phone Dao
public interface PhoneDao {
public Phone save(Phone phone);
}
PhoneDaoImpl
#Repository
public class PhoneDaoImpl implements PhoneDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public Phone save(Phone phone) {
return entityManager.merge(phone);
}
}
PersonDaoImpl
#Repository
public class PersonDaoImpl implements PersonDao{
#PersistenceContext
private EntityManager entityManager;
#Override
public Person save(Person person) {
return entityManager.merge(person);
}
}
Test Method
#Test
#Transactional
#Commit
public void say()
{
Phone phone = new Phone();
phone.setNumber("jghjkhk");
Person person = new Person();
person.setUsername("7576");
phone.setPerson(person);
Phone pers = phoneDao.save(phone);
Assert.assertNotNull(pers);
}
Try now. It will work.
I think that you need to set the value of the person->id and then also use an getter method to pass the id to your phone object instead of passing the person object
Normally people have hibernate set the id of an entity automatically with a surrogate key.
public class Person {
#Id #GeneratedValue // should pick an appropriate strategy here
private long id;
Since you don't have that you must either add it or set it yourself.
Person p = new Person();
p.setId(1); // hopefully unique
The same goes for phone.
As you are not having any generation type on your #Id and id is the primary key which can not be null so either you have to set value of id or have #GeneratedValue annotation on your id field and set strategy either as Auto or Identity.
You can also have your own sequence generation.
Also, you need to do same for the Phone class.

How to access fields of #EmbeddedId?

I have an #Entity that has a composite primary key. Therefore I created an #EmbeddedId holding the PK fields.
Question: is it better to access those fields by id.* directly, or should I create getter/setter in the parent class?
Example:
#Entity
public class MyPerson {
#EmbeddedId
private PersonId id;
public String getFirstname() {
return id.getFirstname();
}
public String getLastname() {
return id.getLastname();
}
public LocalDate getDob() {
return id.getDob();
}
}
#Embeddable
public class PersonId implements Serializable {
private String firstname;
private String lastname;
private LocalDate dob;
//getter+setter as well
}
Should I better use person.getFistname() or person.getId().getFirstname()?
The former is more clear, but with the drawback that I'd have to create the getters both in MyPerson and in PersonId.
What should be the preferred way to access, and why?
You do not have to create property
private PersonId id
Alternative is
#Entity #IdClass(PersonId.class)
public class MyPerson {
#Id private String firstname;
#Id private String lastname;
#Id private LocalDate dob;
// Getters and setters here
}
And use PersonId only for loading MyPerson entities:
PersonId peronId = new PersonId(firstname, lastname, dob);
MyPerson person = session.get(MyPerson.class, personId);

JPA: how to override column names of #Embedded attributes

Person class
#Embeddable
public class Person {
#Column
public int code;
//...
}
is embedded inside Event twice as two different attributes: manager and operator
#Entity
public class Event {
#Embedded
#Column(name = "manager_code")
public Person manager;
#Embedded
#Column(name = "operator_code")
public Person operator;
//...
}
This should give two respective columns when generating database schema with Persistence. Instead an exception is thrown:
org.hibernate.MappingException: Repeated column in mapping for entity: Event column: code
How to override default column name code for each attribute?
Use #AttributeOverride, here is an example
#Embeddable public class Address {
protected String street;
protected String city;
protected String state;
#Embedded protected Zipcode zipcode;
}
#Embeddable public class Zipcode {
protected String zip;
protected String plusFour;
}
#Entity public class Customer {
#Id protected Integer id;
protected String name;
#AttributeOverrides({
#AttributeOverride(name="state",
column=#Column(name="ADDR_STATE")),
#AttributeOverride(name="zipcode.zip",
column=#Column(name="ADDR_ZIP"))
})
#Embedded protected Address address;
...
}
In your case it would look like this
#Entity
public class Event {
#Embedded
#AttributeOverride(name="code", column=#Column(name="manager_code"))
public Person manager;
#Embedded
#AttributeOverride(name="code", column=#Column(name="operator_code"))
public Person operator;
//...
}

I cannot get One-to-one relationship and inheritance to work simultaneously in Hibernate

I am new to Hibernate and learning all the different annotations can be frustrating at times. Currently, I am stuck on making Doctor extend Person and also have a 1-to-1 relationship between Doctor and Specialty. I've been stuck on this for a while and still cannot figure this one out. I've tried testing out one of the two relationships and my code works fine, but I encounter a problem when I put everything together.
this is the error I'm getting:
Exception in thread "main" org.hibernate.MappingException: Could not
determine type for: edu.cs157b.medicalSystem.Specialty, at table:
Person, for columns: [org.hibernate.mapping.Column(specialty)]
Doctor:
package edu.cs157b.medicalSystem;
import javax.persistence.*;
#Entity
public class Doctor extends Person {
#OneToOne
#JoinColumn(name = "SPECIALTY_ID")
private Specialty specialty;
private double salary;
public void setSalary(double salary) {
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSpecialty(Specialty specialty) {
this.specialty = specialty;
}
public Specialty getspecialty() {
return specialty;
}
}
Speciality:
package edu.cs157b.medicalSystem;
import javax.persistence.*;
#Entity
public class Specialty {
#OneToOne
private Doctor doctor;
#Id
#GeneratedValue
#Column(name = "SPECIALTY_ID")
private int sId;
private String specialtyTitle;
public void setSId(int sId) {
this.sId = sId;
}
public int getSId() {
return sId;
}
public void setSpecialtyTitle(String specialtyTitle) {
this.specialtyTitle = specialtyTitle;
}
public String getSpecialtyTitle() {
return specialtyTitle;
}
public void setDoctor(Doctor doctor) {
this.doctor = doctor;
}
public Doctor getDoctor() {
return doctor;
}
}
Person:
package edu.cs157b.medicalSystem;
import javax.persistence.*;
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Person {
private int personId;
private String first_name;
public Person() {
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "PERSON_ID")
public int getPersonId() {
return personId;
}
public void setPersonId(int personId){
this.personId = personId;
}
public void setFirstName(String first_name) {
this.first_name = first_name;
}
public String getFirstName() {
return first_name;
}
}
There are two errors in your code.
First, you annotated the getter in Person, and annotated the field in its subclass Doctor. That's why you get this error: once Hibernate sees the #Id annotation on a getter in the base class, it only considers annotations on getters in the rest of the class hierarchy, and ignores the annotations on fields.
Second, your OneToOne bidirectional association is mapped incorrectly. One side must always be the inverse side in a bidirectional association. So, the following field:
#OneToOne
private Doctor doctor;
should be
#OneToOne(mappedBy = "specialty")
private Doctor doctor;
to inform JPA that the Specialty.doctor association is the inverse side of the OneToOne association already declared and mapped in Doctor.specialty.

Hibernate Annotations - #MappedSuperclass How to override column identifier

I'm working with Hibernate Annotations and the issue that I'm trying to solve goes as follows:
I need to have 2 different #Entity classes with the same columns mapping but with a different Identifier.
The first one should use id as identifier.
The second should use name as identifier.
So, I have an abstract class, annotated with #MappedSuperclass that have all of the columns including id and name, and in addition 2 #Entity classes that extends the super class and overriding the getters of the id and name.
#MappedSuperclass
public class MappingBase {
protected Integer id;
protected String name;
#Column (name = "ID")
public void getId() {
return this.id;
}
#Column (name = "NAME")
public void getName() {
return this.name;
}
}
#Entity
#Table (name = "TABLE")
public class Entity1 extends MappingBase {
#Id
#Column (name = "ID")
public void getId() {
return this.id;
}
}
#Entity
#Table (name = "TABLE")
public class Entity2 extends MappingBase {
#Id
#Column (name = "NAME")
public void getName() {
return this.name;
}
}
Note: I must have the members (id,name) in the super class.
I know that i can add #Transient to the id and name getters but this means that i must add both of them in each class and it's not a good design :(
In addition, the following insertable="false, updateable=false can help but i don't understand what is the meaning of this...
Please help me!
Hibernate/JPA allows us to annotate either properties or accessors. If we have #Id annotation on a property, JPA will lookup all the properties of the class. Similarly, if we have #id annotation on a getter method, JPA will lookup all the getters.
We can solve the above problem by annotating properties instead. The superclass and the two subclasses will be as follows-
#MappedSuperclass
public abstract class AbstractMappingBase {
//properties other than id and name
public abstract Integer getId();
public abstract String getName();
//other getters and setters
}
#Entity
public class Entity1 extends AbstractMappingBase {
#Id
private Integer id;
private String name;
#Override
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
public class Entity2 extends AbstractMappingBase {
private Integer id;
#Id
private String name;
#Override
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here JPA will look for properties instead of getters. There are no duplicate properties between superclass and its subclasses. So it will work fine.
You are much better off defining your base class as #Embeddable and using #Embedded in your implementation classes with the use of #AttributeOverride.
If i remember correctly, I simply defined 2 #Entity classes with the same table that inherits from one abstract #MappedSuperclass class. The super class contains the id member and each Entity class define it's own #Id #Column definition. It should work!

Categories