is it possible to bind a String attribute of an JavaClass to a column of a different table.
Example
#Entity
#Table(name = "ACCOUNTS")
public class Account {
private Long id;
private String nickname;
private String address;
#Id
#Column(name = "A_ID")
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name="A_NICKNAME")
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
What would be the correct annotation if the address where a String in a separate table?
Separate Table
CREATE TABLE IF NOT EXISTS `Adresses` (
`ad_id` BIGINT NOT NULL AUTO_INCREMENT ,
`ad_address` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`da_id`) )
ENGINE = InnoDB;
thanks
You can't define address field of your object to be stored in another table (it has its own identifier that you don't have it in your account class ) but you can define another class for address and let it have its own mapping and table then your account class will have a many to one relationship with address class.This approach can have advantages too.What if you want to add another fields to your address table ? and if there's no chance that other fields can be added to your address table why do you want to store it in a separate table ?
Related
Can I save an object that contains another object directly in the database?
My back-end structure is like this:
Rest services
Services
Repository (extends JpaRepository)
Model
Suppose that I have two entity in my model: Company and Address. Both generated by the JPA tool provided by IntelliJ.
Company class model
#Entity
public class Company {
private int idcompany;
private String name;
private Address address;
#Id
#Column(name = "idcompany")
public int getIdcompany() {
return idcompany;
}
public void setIdcompany(int idcompany) {
this.idcompany = idcompany;
}
#Basic
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#ManyToOne
#JoinColumn(name = "idaddress", referencedColumnName = "idaddress", nullable = false)
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address class model
#Entity
public class Address {
private long idaddress;
private String zip;
#Id
#Column(name = "idaddress")
public long getIdaddress() {
return idaddress;
}
public void setIdaddress(long idaddress) {
this.idaddress = idaddress;
}
#Basic
#Column(name = "zip")
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
}
Moreover, both entities have an interface that extends the interface JpaRepository<T,ID>. So, I have CompanyRepository and AddressRepository. I use these two interfaces in they respective Service classes: CompanyService and AddressService. This is where I put the business logic. All ok!
Now, I recive using a REST service, through POST an object Company that contains the object Address. I need to save them into the database (MySql).
In the json file there are Company that contains Address!
Until now, I've always done these steps:
Save Address;
Retrieve the Address just saved (i need the idaddress);
I associate the Address to the company using setAddress;
Save Company
I tried to save the object Company received via REST calling the method save from CompanyService (using CompanyRepository) but I got the error:
Column 'idaddress' cannot be null
I ask you. Is there an easier way to save Company and Address at the same time using JpaRepository???
You don't have defined any cascading.
You could define PERSIST to let the Address persist when Company is persisted:
#ManyToOne
#JoinColumn(name = "idaddress", referencedColumnName = "idaddress", nullable = false,
cascade = CascadeType.PERSIST)
public Address getAddress() {
return address;
}
For every method on the EntityManager there is a CascadeType defined.
Read more about the cascade types here:
https://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/
I' currently trying to build a complete Spring Boot Rest service with jdbc connection by myself.
At the moment I'm struggling with a minor problem of comprehension regarding hibernate and storing entities.
I have one base class:
#Entity
#Table
public abstract class Person {
private int id;
private String firstName;
private String middleName;
private String lastName;
#Id
#GeneratedValue(generator = "increment")
#GenericGenerator(name = "increment", strategy = "increment")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#Column
public String getMiddleName() {
return middleName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
#Column
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
And 2 sub classes:
#Entity
#Table
public class Member extends Person{
private String memberNumber;
#Column
public String getMemberNumber() {
return memberNumber;
}
public void setMemberNumber(String memberNumber) {
this.memberNumber = memberNumber;
}
}
and
#Entity
#Table
public class Supporter extends Person {
private String supporterNumber;
#Column
public String getSupporterNumber() {
return supporterNumber;
}
public void setSupporterNumber(String supporterNumber) {
this.supporterNumber = supporterNumber;
}
}
The base class is abstract because I want to prevent to create a instance of this without specify a person either a member or supporter. But in the database scheme I still want to have 3 tables because of normalization.
Which annotations should I use now the reach this target? How can I link a row of member or supporter to the member now? I'm really confused.
Thanks!
The mapping from class to table is done by hibernate. Since a relational database table and a Java object are kinda different ORM mappers have different strategies how to map between them.
Hibernate can use the following strategies:
MappedSuperclass
Single Table (default)
Joined Table
Table per class
You can read more about them from the official documentation.
They have different pros and cons and normally it is safest to just use the default. However the default strategy only uses one table so you need to switch to an other strategy.
The Table per class will create three tables. You can also check the examples for MappedSuperclass and Joined Table which will also use multiple tables.
From the official documentation:
#Entity(name = "Account")
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public static class Account {
#Id
private Long id;
private String owner;
private BigDecimal balance;
private BigDecimal interestRate;
//Getters and setters are omitted for brevity
}
#Entity(name = "DebitAccount")
public static class DebitAccount extends Account {
private BigDecimal overdraftFee;
//Getters and setters are omitted for brevity
}
#Entity(name = "CreditAccount")
public static class CreditAccount extends Account {
private BigDecimal creditLimit;
//Getters and setters are omitted for brevity
}
Will create these tables:
CREATE TABLE Account (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
PRIMARY KEY ( id )
)
CREATE TABLE CreditAccount (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
creditLimit NUMERIC(19, 2) ,
PRIMARY KEY ( id )
)
CREATE TABLE DebitAccount (
id BIGINT NOT NULL ,
balance NUMERIC(19, 2) ,
interestRate NUMERIC(19, 2) ,
owner VARCHAR(255) ,
overdraftFee NUMERIC(19, 2) ,
PRIMARY KEY ( id )
)
I'm creating a system where I have some entities that have some properties that are common, like address (with street, number, zip etc.) and phone (number, type, etc.) and I don't want to repeat these columns on each entity.
Here' a example:
Student has address and phone
Teacher has multiple addresses (home
and office) and multiple phones (home, mobile, office)
StaffMember
has address and multiple phones (home, mobile and office)
I've used something like that while developing Ruby On Rails using polymorphic associations. I've searched for something like on Java/JPA/Hibernate and couldn't find something much like it. I've found many things about JPA inheritance but I don't quite understand it.
Can you give me a example on how to model it and how to use it?
EDIT
After reading my question I think it's not clear enough, so let me add here the database schema I have:
Student
-------
id bigint
name varchar
birth_date date
...
Teacher
-------
id bigint
name varchar
birth_date date
department varchar
...
StaffMember
-------
id bigint
name varchar
birth_date date
department varchar
function varchar
...
Address
-------
id bigint
street varchar
number int
...
entity_id bigint
entity_type varchar
Phone
-----
id bigint
type varchar
number varchar
...
entity_id bigint
entity_type varchar
And for both Address and Phone the columns entity_id and entity_type are references to Student, Teacher and StaffMember.
But how to map it using Hibernate/JPA?
If you don't mind using Hibernate-specific annotations, there are the #Any and #AnyDef annotations that do what you want. You will need to specify a #MetaValue entry for each entity type related to Address so Hibernate knows how to store the proper value for item_type
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.MetaValue;
#Entity
public class Address {
#Any(metaColumn = #Column(name = "ITEM_TYPE"))
#AnyMetaDef(idType = "long", metaType = "string",
metaValues = {
#MetaValue(targetEntity = Student.class, value = "STUDENT"),
#MetaValue(targetEntity = Teacher.class, value = "TEACHER")
})
#JoinColumn(name="ITEM_ID")
private Object item;
#Id
#GeneratedValue
private Long id;
#Column
private String type;
#Column
private String street;
#Column
private int number;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Object getItem() {
return item;
}
public void setItem(Object item) {
this.item = item;
}
#Override
public String toString() {
return "Address{" +
"person=" + item +
", id=" + id +
", type='" + type + '\'' +
", street='" + street + '\'' +
", number=" + number +
'}';
}
}
Now, you can just use Address in any bean that has the proper #MetaValue entry, both as #ManyToOne association:
#ManyToOne
protected Address address;
Or a #OneToMany association:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Address> addresses;
I created a simple test project using Spring Boot and it seemed to work nicely!
I think what you need is to use a generic repository class.
This post http://blog.netgloo.com/2014/12/18/handling-entities-inheritance-with-spring-data-jpa explains pretty much it. Let me know if works for you.
Something along these lines:
#Embeddable
public class Address {
// Fields & accessors
// Do the Phone class in similar fashion
}
#Entity
#DiscriminatorColumn(name = "entity_type")
public abstract class Person {
#Id
private Integer id;
private String name;
#Embedded
private Address homeAddress;
#Embedded
private Phone homePhone;
// Other shared fields & accessors
}
#Entity
public abstract class Employee extends Person {
#Embedded
private Phone mobilePhone;
#Embedded
private Phone officePhone;
//...
}
#Entity
public class Students extends Person {
}
#Entity
public class StaffMember extends Employeee {
}
#Entity
public class Teacher extends Employeee {
#Embedded
private Address officeAddress;
// Accessors & other ...
}
I have nested models in a very simple Play application. I have a User model which looks like;
#Entity
public class User extends Model {
#Id
public Integer id;
#Constraints.Email
#Constraints.Required
public String email;
#Constraints.Required
private String password;
#ManyToOne
public City city;
}
And the City model looks like;
#Entity
public class City extends Model {
#Id
public Integer id;
public String name;
#ManyToOne
public Country country;
}
Which is again, very simple.
I then have the Country model, which is;
#Entity
public class Country extends Model {
#Id
public Integer id;
public String name;
}
Now, what I'm doing is POST-ing parameters email, password, and city_id to an action;
public static Result registerUser() {
Form<Register> registerForm = form(Register.class).bindFromRequest();
Logger.debug(registerForm.toString());
if (registerForm.hasErrors()) {
return badRequest(register.render(registerForm));
} else {
User user = form(User.class).bindFromRequest().get();
user.save();
return redirect(controllers.routes.Application.login());
}
}
The database I'm using is MySQL, and I can see the new User rows coming in. What I always see is that city_id stays null which wasn't what I had assumed.
I had assumed Hibernate to take care of the relationship between the objects and the corresponding database foreign keys, but that doesn't seem to be working.
I have a city with id = 1 entered into the city table already and that is the city_id I'm sending through POST.
What's going on here?
The following query throws the exception:
Query query = session.createQuery("from Associate as a order by a.username asc");
associates = query.list();
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [ca.mypkg.model.Associate#0]
If I create an entry in the database with id of 0 it works just fine. I don't really get it because I'm just trying to load all the entries in the db not just a specific one.
Similar questions I've found have been concerned with trying to load an object with a given ID I'm doing no such thing.
Associate class:
#Table(name = "user")
#XmlRootElement(name = "associate")
public class Associate implements Serializable {
private String username;
private String password;
private String firstName;
private String lastName;
private String userType;
private int id;
private String email;
private String isActive;
private Department dept;
private String lastUpdated;
private String associate_type;
// ...
#Id
#GeneratedValue
public int getId() {
return id;
}
#OneToOne
#JoinColumn(name = "dept")
public Department getDept() {
return dept;
}
From my experience this type of error message usually means it does not find joined entity by mentioned id, and not the entity requested in the query (Associate, in your case).
My guess is that Associate class contains a join entity which has primitive type primary key.