Consider this regular javabean without ORM/:
// primary key is auto incremented by the database, so I can't add it
public class User {
String name;
int personID; // foreign key
// no args constructor
// getter/setters for fields
}
Is it ok to do this? I personally think it doesn't make sense because there is no reason to manipulate the foreign key through the bean, but I might be wrong. Are there use cases where this is good?
I would normally do it like this.
public class Person {
private final String id;
public Person(String id) {
this.id = id;
}
public Person() {
this.id = UUID.randomUUID().toString();
}
}
and
public class User {
private final String id;
private String personId;
public User(String id, String personId) {
this.id = id;
this.personId = personId;
}
public User() {
this.id = UUID.randomUUID().toString();
}
public String getPersonId() {
return personId;
}
public void setPersonId(String personId) {
this.personId = personId;
}
}
Another alternative for User is to make it immutable. In which case it would look something like this.
public class User {
private final String id;
private final String personId;
public User(String id, String personId) {
this.id = id;
this.personId = personId;
}
public User(String personId) {
this.personId = personId;
this.id = UUID.randomUUID().toString();
}
public String getPersonId() {
return personId;
}
}
Now the classes can be used in an autoincremented or non-autoincremented way. Basically the option is there for the classes to make their own unique ID or for a unique ID to be passed to it.
One common dilemma that I have seen is when the id does not exist yet the object does. That is the case when the object is created in the program but the ID (which maybe created by the DB when it is inserted) is not assigned to it yet. In that case the bean may look like this.
public class User {
private String id;
private final String personId;
public User(String id, String personId) {
this.id = id;
this.personId = personId;
}
public User(String personId) {
this.personId = personId;
}
public String getPersonId() {
return personId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Notice the id is not final and hence the object can exist without the id present and can be added in later if needed
You are better off modelling the objects and their relationships to one another in an object oriented way.
public class User {
private long id;
private Person person;
// .... Removed for clarity
}
Related
Recently I switched into Spring Data JPA and I am wondering how is it possible to save a new object into the database with some default values.
In my example I have to save a book into my database, but, in the owner column, I need to put the value 0.
So, this how I did that with JDBC, and it works amazingly well.
public void save(Book book){
jdbcTemplate.update("INSERT INTO book(name,author,yearOfProduction,owner) VALUES (?, ?, ?, 0)",
book.getName(),book.getAuthor(),book.getYearOfProduction());
}
Now I want to do the same with Spring Data JPA. Here is my save function:
BookService
#Transactional
public void save(Book book)
{
bookRepository.save(book);
}
I have two objects: Person and Book. The relationships are: one person can have many books and one book has one owner. Here are my Person and Book classes:
Book
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id",nullable = false)
private int id;
#Column(name="name")
#NotEmpty(message = "Book name can't bee empty")
private String name;
#Column(name="author")
#NotEmpty(message = "Author name can't bee empty")
private String author;
#Column(name="yearOfProduction")
#NotNull(message = "Year of production can't bee empty")
#Min(value = 0,message = "year must be more than 1900")
private int yearOfProduction;
#ManyToOne
#JoinColumn(name = "owner_id", referencedColumnName = "id")
private Person owner;
public Book(String name, String author, int yearOfProduction) {
this.name = name;
this.author = author;
this.yearOfProduction = yearOfProduction;
}
public Book(){
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
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 String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getYearOfProduction() {
return yearOfProduction;
}
public void setYearOfProduction(int yearOfProduction) {
this.yearOfProduction = yearOfProduction;
}
}
Person|
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
#NotEmpty(message = "Please enter the name")
#Size(min = 1, max = 30, message = "Length must be 2-30 symbols")
//make regex with ФИО;
private String name;
#Column(name = "ageOfBirth")
#Min(value = 0, message = "age of birth must be more than 0")
private int ageOfBirth;
#OneToMany(mappedBy = "owner")
private List<Book> books;
public Person() {
}
public Person(String name, int ageOfBirth) {
this.name = name;
this.ageOfBirth = ageOfBirth;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
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 int getAgeOfBirth() {
return ageOfBirth;
}
public void setAgeOfBirth(int ageOfBirth) {
this.ageOfBirth = ageOfBirth;
}
}
I guess this is impossible to make with Spring Data JPA? So, I made it with by adding JdbcTemplate and I definitely think it is a hard-coding approach to use Spring DATA and JdbcTemplate together.
It's also unable to make with database. Down below i am using the default definition of postgres and still get null's when create a new book.
https://www.baeldung.com/jpa-default-column-values
create table book(
id int primary key generated by default as identity,
name varchar(250) not null,
author varchar(250) not null,
yearOfProduction int not null,
owner_id int default 0 references person(id)
)
#egeorge answered my question. It is impossible to input 0 into owner table.
Since 0 has a special value, and is not a real person record, there should not be any value in that field. null is the appropriate value for a join column that is not joined to an actual record. You will need to change the logic that interprets 0 as "free" to check for null instead. (I am surprised your database let you do that to begin with. Foreign key constraints will normally reject a value that is not present in the referred table.)
I have a next question: while working with Hibernate 3.3.0 run into a situation when I have two tables with one-to-many relationships and I need to get the list of parents. In each entity must be filled the several fields from the parent table and a list of all children mapped in the parent. For the easiest understanding, I give an example. I have two tables with one-to-many relationships: parent is "recipients" and child is "requisites". And I have two classes whose objects are the rows of these tables. Class for the table of recipients:
#Entity
#Table(name = "recipients")
#JsonFilter(value = "recipientsFilter")
public class POJORecipient implements POJO {
private static final long serialVersionUID = 4436819032452218525L;
#Id
#Column
private long id;
#Version
#Column
private long version;
#Column(name = "client_id")
private long clientId;
#Column
private String inn;
#Column
private String name;
#Column(name = "rcpt_country_code")
private String rcptCountryCode;
#Column(name = "rcpt_passp_ser")
private String rcptPasspSer;
#Column(name = "rcpt_passp_num")
private String rcptPasspNum;
#OneToMany(mappedBy = "recipient", fetch = FetchType.LAZY)
#JsonManagedReference
private Set<POJORequisite> requisites = new HashSet<>();
public POJORecipient(){}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public long getClientId() {
return clientId;
}
public void setClientId(long clientId) {
this.clientId = clientId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInn() {
return inn;
}
public void setInn(String inn) {
this.inn = inn;
}
public String getRcptCountryCode() {
return rcptCountryCode;
}
public void setRcptCountryCode(String rcptCountryCode) {
this.rcptCountryCode = rcptCountryCode;
}
public String getRcptPasspSer() {
return rcptPasspSer;
}
public void setRcptPasspSer(String rcptPasspSer) {
this.rcptPasspSer = rcptPasspSer;
}
public String getRcptPasspNum() {
return rcptPasspNum;
}
public void setRcptPasspNum(String rcptPasspNum) {
this.rcptPasspNum = rcptPasspNum;
}
public Set<POJORequisite> getRequisites() {
return requisites;
}
public void setRequisites(Set<POJORequisite> requisites) {
this.requisites = requisites;
}
}
and for requisites table:
#Entity
#Table(name = "requisites")
public class POJORequisite implements POJO {
private static final long serialVersionUID = -35864567359179960L;
#Id
#Column
private long id;
#Version
#Column
private long version;
#Column
private String bic;
#Column
private String bill;
#Column
private String comments;
#Column
private String note;
#ManyToOne
#JoinColumn(name = "recipient_id")
#JsonBackReference
private POJORecipient recipient;
public POJORequisite(){}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public String getBic() {
return bic;
}
public void setBic(String bic) {
this.bic = bic;
}
public String getBill() {
return bill;
}
public void setBill(String bill) {
this.bill = bill;
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public POJORecipient getRecipient() {
return recipient;
}
public void setRecipient(POJORecipient recipient) {
this.recipient = recipient;
}
}
So, I want to select from the recipients only names and all mapped requisites. Consequently, after the selection, I will have a list of POJORecipient objects and in each object filled only the field "name" and set of POJORequisite objects.
As answer of my question I want to discover one of next: how can I do that with help HQL or Criteria API (the second variant is preferable), or understand it is impossible in Hibernate at all, or that this possibility appeared in later versions (also preferably with example). I'm trying to resolve this question for several months now and will be immensely grateful for any help. All clarifications and advices also will be so helpful. Thanks in advance!!!
I have two tables in program
#Entity
#Table(name="managerlog")
public class Manager {
private Integer id;
private Address address;
#Id
#Column(name="id")
#GeneratedValue
public Integer getId() {
return id;
}
#OneToOne(mappedBy="mng")
public Address getAddress() {
return address;
}
public void setId(Integer id) {
this.id = id;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address pojo class
#Entity
#Table(name="Address")
public class Address {
private Integer id;
private String locality;
private Manager mng;
public Address()
{
}
public Address(String locality) {
this.locality = locality;
}
public Address(Integer id,String locality) {
this.id=id;
this.locality = locality;
}
#Id
#GeneratedValue
#Column(name="id")
public Integer getId() {
return id;
}
#Column(name="locality")
public String getLocality() {
return locality;
}
#OneToOne
#JoinColumn(name="MID")
public Manager getMng() {
return mng;
}
public void setLocality(String locality) {
this.locality = locality;
}
public void setId(Integer id) {
this.id = id;
}
public void setMng(Manager mng) {
this.mng = mng;
}
}
Address table in db is Manager(id,locality,mid)
Problem is i want to access address id based on mid with hibernate query like
Query qry=session.createQuery(" address.id From Address address where address.mid=:MID");
and i set the parameter .. giving me error
Exception in thread "main" java.lang.IllegalArgumentException: node to traverse cannot be null!
at org.hibernate.hql.ast.util.NodeTraverser.traverseDepthFirst(NodeTraverser.java:63)
..
Here I know the way of getting Manager class ..from that class getting address.. but if i have MID(foreign key) ..can't I get the address.id(primary key)..
Can anyOne explain me ??
The main problem is that the Address class doesn't have the property mid, bacause mid is just the column of the your database structure. If you want to get the address for that manager, you have to change the query in this way:
Query qry=session.createQuery(" address.id From Address address where address.mid.id=:MID");
in this case you can set the manager identificator and all should works
I am really newbie to Hibernate and it's been like two hours trying to figure it out how to fix this issue. I am using Hibernate 4 and Postgres 9.3
Given the CatalogBase class
#MappedSuperclass
public class CatalogBase {
#Id
#Type(type = "pg-uuid")
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
protected UUID id;
}
And the derived User class
#Entity
#Table(name="erpuser")
public class User extends CatalogBase {
private String lastName;
private String name;
private String email;
private boolean isSystemAdministrator;
#Type(type="org.hibernate.type.StringClobType")
#Column(nullable = false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name="lastname")
#Type(type="org.hibernate.type.StringClobType")
#NotNull(message = "es campo mandatorio")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Column(length = 100,unique = true)
#NotNull(message = "es campo mandatorio")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Column(name = "issystemadministrator", nullable = false)
public boolean isSystemAdministrator() {
return isSystemAdministrator;
}
public void setSystemAdministrator(boolean isSystemAdministrator) {
this.isSystemAdministrator = isSystemAdministrator;
}
}
I am trying to filter just the first result of a query using Hibernate Criteria. Like this
public boolean existsSystemAdministrator() throws NoSuchAlgorithmException{
Criteria criteria=currentSession()
.createCriteria(User.class)
.add(Restrictions.eq("isSystemAdministrator", true));
return criteria.uniqueResult() != null;
}
But I always get org.hibernate.QueryException: could not resolve property: isSystemAdministrator exception
I have changed to all lowercase since the database field is like that, but it didn't work either. From what I've read Hibernate maps with the Java property, which hasn't been the case as well.
Have tried also change the isSystemAdministrator field to Boolean instead of boolean, but it didn't work out either.
I know this must sound stupid to any Hibernate guru, if someone can come up with an answer that would save me lots of time.
Thanks in advance.
You should adhere to the JavaBeans spec (http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html). The field should be 'systemAdministrator', and the method should be 'isSystemAdministrator'.
The problem is in #Id annotation in CatalogBase class. If you change so it will work fine:
#MappedSuperclass
public class CatalogBase {
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
#Id
#Type(type = "pg-uuid")
protected UUID id;
}
You can have 2 access types in Hibernate. Property access (as you did) or field access. Hibernate will guess the access type from the position of #Id or #EmbeddedId.
As I know (I am not a Hibernate guru), it should be no difference between these two access types. But some frameworks requires to have field access. Anyway, I do not know why your implementation does not work for querying and have not found any other explanation.
I have model. there is this part:
model was mapped by jpa annotations.Everywhere I use fetchType = EAGER. If I load vacancy from database, I have 2 duplicates status_for_vacancy objects.
I use property hbm2ddl.auto = update.
If I make new schema of database and fill data, I haven't duplicates status_for_vacancy objects.
It really?
code:
vacancy:
#Entity
#Table(name = "vacancy")
#XmlRootElement(name="vacancy")
public class Vacancy {
private List<VacancyStatus> statusList = new LinkedList<VacancyStatus>();
#OneToMany(mappedBy = "vacancy", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<VacancyStatus> getStatusList() {
return statusList;
}
public void setStatusList(List<VacancyStatus> statusList) {
this.statusList = statusList;
}
}
status_for_vacancy:
#Entity
#Table(name = "status_for_vacancy")
public class StatusForVacancy extends AbstractStatus {
public StatusForVacancy() {
super();
}
public StatusForVacancy(Integer id, String name) {
super(id, name);
}
}
#MappedSuperclass
#XmlRootElement
public abstract class AbstractStatus {
private Integer id;
private String name;
public AbstractStatus() {
super();
}
public AbstractStatus(String name) {
super();
this.name = name;
}
public AbstractStatus(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column (name ="id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "name")
#NotEmpty
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
vacancy_status:
#Entity
#Table(name = "vacancy_status")
public class VacancyStatus extends AbstractHistoryStatus {
private Vacancy vacancy;
private StatusForVacancy status;
public VacancyStatus() {
super();
}
public VacancyStatus(Integer id, User author, Date date,
Vacancy vacancy, StatusForVacancy status) {
super(id, author, date);
this.vacancy = vacancy;
this.status = status;
}
#ManyToOne
#JoinColumn(name = "vacancy_id")
public Vacancy getVacancy() {
return vacancy;
}
public void setVacancy(Vacancy vacancy) {
this.vacancy = vacancy;
}
#ManyToOne
#JoinColumn(name = "status_id")
public StatusForVacancy getStatus() {
return status;
}
public void setStatus(StatusForVacancy status) {
this.status = status;
}
}
#MappedSuperclass
public abstract class AbstractHistoryStatus {
private Integer id;
private User author;
private Date date;
public AbstractHistoryStatus() {
}
public AbstractHistoryStatus(Integer id, User author, Date date) {
super();
this.id = id;
this.author = author;
this.date = date;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
#Column(name="creation_date")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
It is all mapping code for these entities.
in debugger:
both id==500 ==> hibernate understand, that it is same objects.
I try add all data from old database to new database - I get old error(
I fix cause of appearance of this problem. It appearances if I add record to note table:
I highly recommend you write equals() and hashCode() methods. The standard equals()/hashCode() implement referential equality (do 2 objects reference the same memory location). So if hibernate has 2 of the 'same' object in memory, but they don't reference the same memory location then you will see the object show up twice. But if you implement equals() based on primary key being equal, then even if there are two copies of the same object in memory, Hibernate won't give you duplicates.
See the JPA spec:
2.4 Primary Keys and Entity Identity
Every entity must have a primary key. ... The value of its primary key
uniquely identifies an entity instance within a persistence context
and to EntityManager operations
Also see this SO post.