Unable to create One to many mapping table in hibernate - java

I am quite new to hibernate. I have created two entities like user and vehicle with user having one to many relationship with vehicle.
#OneToMany
#JoinColumn(name="Vehicle_id")
Collection<Vehicle> vehicle = new ArrayList<>();
and adding them to table like this
UserInfo user = new UserInfo();
user.setUsername(username);
user.setPassword(password);
user.setDob(dob);
Vehicle vehicle = new Vehicle();
vehicle.setVehicleName("AUdi");
user.getVehicle().add(vehicle);
Vehicle vehicle2 = new Vehicle();
vehicle2.setVehicleName("BMW");
user.getVehicle().add(vehicle2);
SessionFactory sessionfactory = new AnnotationConfiguration().configure().buildSessionFactory();
Session session = sessionfactory.openSession();
session.beginTransaction();
session.save(user);
session.save(vehicle);
session.save(vehicle2);
session.getTransaction().commit();
session.close();
But I am getting result like
Hibernate: insert into UserInformation (user_name, DOB) values (?, ?)
Hibernate: insert into Vehicle (vehicleName) values (?)
Hibernate: insert into Vehicle (vehicleName) values (?)
Hibernate: update Vehicle set Vehicle_id=? where vehicleID=?
Hibernate: update Vehicle set Vehicle_id=? where vehicleID=?
There is no table created like
insert into User_vehicle(User_id,vehicle_id) Values (?,?)
So I am not getting any table name User_vehicle in db.
Hope you understand my question.

You have specified #JoinColumn for a #OneToMany association, thus there will be a foreign key column on the many side. That is the recommended approach actually.
If you need to use join table for #OneToMany association, then you need to omit #JoinColumn and optionally specify #JoinTable to override the default names for the table and columns:
#OneToMany
#JoinTable(
name="User_Vehicle",
joinColumns = #JoinColumn(name = "User_Id"),
inverseJoinColumns = #JoinColumn(name = "Vehicle_Id")
)
Collection<Vehicle> vehicles = new ArrayList<>();

Related

Is it possible to create queries with AND without entity classes in the same method? (JPA + Hibernate)

I have a query method that I am attempting to define, however I am currently facing a problem. The query I am attempting to make is one that gets all users in my DB but filters out users with an admin role. I have an entity class for User and another for Role. My User class has fields for a users id, username, and email while the 'Role' entity contains a role id and role name as fields. Inside my user class entity, I define a many to many relationship defined as follows:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
The catch is that this is also where the user_roles table is generated, so I do not have an entity class related to user_roles.
So basically I am wondering if it is possible to create a query that uses values with and without entitles in the same method and if I need to use prepared statements in it?
#Query("SELECT u.id,u.username,u.email FROM User u,Role r, user_role ur WHERE u.id=ur.id AND r.id=ur.id AND r.name<>\'ADMIN\'")
List<User> getUsers();
I am currently getting an error user_role is not mapped [SELECT u.id,u.username,u.email FROM com.Application.models.User u,com.Application.models.Role r, user_role ur WHERE u.id=ur.id AND r.id=ur.id AND r.name<>'ADMIN'].
You can do this with a JPA inner join. It will traverse the join table for you.
#Query("SELECT u.id, u.username, u.email FROM User u join u.roles r where r.name <> 'ADMIN'")

what is #JoinColumn and how it is used in Hibernate

I have been reading a lot about #JoinColumn but I still don't get the idea behind it.
Patient Table
CREATE TABLE patient (
patient_id BIGINT NOT NULL,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
PRIMARY KEY(patient_id));
Vehicle Table
CREATE TABLE vehicles (
patient_id BIGINT NOT NULL,
vehicle_id BIGINT NOT NULL,
vehicle_manufacturer VARCHAR(255),
PRIMARY KEY (vehicle_id),
CONSTRAINT patienthasmanyvehicle FOREIGN KEY(patient_id) REFERENCES patient(patient_id));
Patient Class
#OneToMany(mappedBy = "patient")
private Collection<Vehicle> patientVehicles = new ArrayList<Vehicle>();
Vehicle Class
#ManyToOne
#JoinColumn(name="patient_id")
private Patient patient;
I'm confused on how the Vehicle class part, what is the relationship between
Vehicle Class ---- Entity
#JoinColumn(name="patient_id") ---- annotation
private Patient patient ----field
Does it say; The Vehicle Entity has a Foreign Key to Patient entity named patient_id.
Add the patient_id as a column in the Vehicle Entity table
Do the name parameter of the JoinColumn should always be a Foreign Key or Primary Key?
I have been reading this but I'm still confuse.
JPA JoinColumn vs mappedBy
A unidirectional association via a join table
#Entity
class Patient {
#OneToMany
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
}
A bidirectional association via a join table
#Entity
class Patient {
#OneToMany
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
private Patient patient;
}
A unidirectional association via a foreign key
#Entity
class Patient {
#OneToMany
#JoinColumn
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
}
A bidirectional association via a foreign key
#Entity
class Patient {
#OneToMany(mappedBy = "patient")
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
private Patient patient;
}
We don't need to use #JoinColumn on the Vehicle side, Hibernate assumes
it by default. Sometimes I use it just to stress it out (another case, when we want to specify a join column name).
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn
private Patient patient;
}
A bidirectional association via a foreign key with a foreign column name specification
#Entity
class Patient {
#OneToMany(mappedBy = "patient")
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="patient_id")
private Patient patient;
}
This is the basic starting point of using #JoinColumn.
To verify that the foreign key(patient_id in the Vehicle table) is really mapped in the patients table you can use #JoinColumn(nullable = false)
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="patient_id", nullable = false)
private Patient patient
}
The join column is declared with the #JoinColumn annotation which looks like the #Column annotation. It has one more parameters named referencedColumnName. This parameter declares the column in the targeted entity that will be used to the join.
In a bidirectional relationship, one of the sides (and only one) has to be the owner: the owner is responsible for the association column(s) update. To declare a side as not responsible for the relationship, the attribute mappedBy is used. mappedBy refers to the property name of the association on the owner side.
Here is Sample code :
EntityOne :
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TEST_ID")
private EntityTwo entityTwo;
EntityTwo :
// bi-directional many-to-one association to EntityOne Here TEST_ID is the Primary key
#OneToMany(mappedBy = "entityTwo")
private List<EntityOne> entityOne;
Vehicle Class ---- Entity
#JoinColumn(name="patient_id") ---- annotation
private Patient patient ----field
Above code will generate a column patient_id (a foreign key) in Vehicle class which will point to Patient Class primary key.
MappedBy - This attribute tells us that this relation will be managed by Vehicle class. Example. If we insert a vehicle, then two SQL will be injected if cascadetype is all/save. 1st SQL will inject details in Patient table and 2nd SQL will inject vehicle details in vehicle table with patient_id column of Vehicle column pointing to Patient tuple inserted.
The table in which join column will be found depends upon context.
If the join is for a OneToOne or ManyToOne mapping using a foreign key mapping strategy, the foreign key column is in the table of the source entity or embeddable.
If the join is for a unidirectional OneToMany mapping using a foreign key mapping strategy, the foreign key is in the table of the target entity.
If the join is for a ManyToMany mapping or for a OneToOne or bidirectional ManyToOne/OneToMany mapping using a join table, the foreign key is in a join table.
If the join is for an element collection, the foreign key is in a collection table.
Why is that the patient_id (generated column which is a FK) in the
Vehicle Table doesn't have any value when I run my code?
All #JoinColumn does is to specify a column for joining an entity association or element collection. Since you have made #JoinColumn associated with Patient class object, that's why foreign key is created on Patient table.
For more please refer https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html

not getting data from the mapping table using hibernate

Hi i am new to hibernate.
I have a java web project and i am using spring mvc with hibernate in this project and for database i am using my sql.
The issue i am facing is that :
i have a table in db as user and in java project as user.java
and i have another table references and in java project i have referances.java
and i have created a mapping table as user_referances_mapping(user_id, referance_id).
and i have one to many relationship between user and referances table.
Now when i try to get the user data it gives me user table coloumns data but not the referances data and returns only null list for referances table.
On the other hand i also have similar mapping tables with user table like role, address with one to many mapping only and data is getting retrieved from those tables.
So can anyone help me getting the solution, that why i am not getting the data of the referances table using the one to many relationship and what should be the solution to it.
mapping of referances table in user table:
#OneToMany
#Basic(optional = true)
#BatchSize(size = 5)
#Cascade(CascadeType.ALL)
#Cache(region = IAppConstants.CACHE_REFERANCES, usage = CacheConcurrencyStrategy.READ_WRITE)
#JoinTable(name = "user_referances_mapping", joinColumns = { #JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "referance_id") })
private List<Referances> referances = new ArrayList<Referances>();
public List<Referances> getReferances() {
return referances;
}
public void setReferances(List<Referances> referances) {
this.referances = referances;
}
UserDao class function :
public User getC2SUserByContactNoOrEmail(final String value) throws ApplicationException {
try{
#SuppressWarnings("unchecked")
Query query = currentSession().createQuery(
IQueryConstants.FETCH_USER_BY_CONTACTNO_OR_EMAIL);
query.setParameter("contactNo", value);
query.setParameter("email", value);
return (User) query.uniqueResult();
}catch(Exception e){
throw new ApplicationException(
"Issue occurred while fetching user by: " + value, e);
}
//return null;
}
FETCH_USER_BY_CONTACTNO_OR_EMAIL = "FROM User WHERE contactNo=:contactNo or email=:email";
If I'm right, the OneToMany relations are defined as "lazy" by default, which means you need to explicitly state that you want to fetch the related records.
Try modifying the query like this:
FETCH_USER_BY_CONTACTNO_OR_EMAIL = "FROM User u LEFT JOIN FETCH u.referances WHERE u.contactNo=:contactNo or u.email=:email";

Why hibernate runs delete and insert commands for embaddable objects

I am trying to create a simple example to understand how collection of basic and embaddable types works in Hibernate.
I have created a User entity with a set of nickNames and also a set of addresses. Here are my Java classes:
User.java
#Entity
#Table(name = "TB_User")
public class User {
#Id
#GeneratedValue
private int id;
private String name;
#ElementCollection
#CollectionTable(name = "Nicknames", joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "nickname")
private Set<String> nickNames = new HashSet<String>();
#ElementCollection
#CollectionTable(name = "Addresses", joinColumns = #JoinColumn(name = "user_id"))
#AttributeOverrides({ #AttributeOverride(name = "street1", column = #Column(name = "fld_street")) })
public Set<Address> addresses = new HashSet<Address>();
public User() {
}
public User(String name, Address... addresses) {
this.name = name;
this.addresses.addAll(Arrays.asList(addresses));
}
public void addNickName(String... nickNames) {
this.nickNames.addAll(Arrays.asList(nickNames));
}
// Setters & Getters
}
Address.java
#Embeddable
public class Address {
private String street1;
public Address() {}
public Address(String street1) {
this.street1 = street1;
}
#Override
public String toString() {
return street1;
}
// Setters & Getters
}
Now I have created a simple program to create users in database and then show the list of users. Here is my code:
private static void saveUsers() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.getTransaction().begin();
User user = new User("User", new Address("abc"),
new Address("xyz"));
user1.addNickName("alpha", "beta");
session.save(user);
session.getTransaction().commit();
}
private static void showUsers() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.getTransaction().begin();
List<User> users = session.createQuery("from User").list();
for (User user : users) {
System.out.println(user.getName() + " -- > " + user.getNickNames()
+ " --> " + user.getAddresses());
}
session.getTransaction().commit();
}
When I run this program, I observed that hibernate issues below set of commands:
Hibernate: select user0_.id as id1_2_, user0_.name as name2_2_ from TB_User user0_
Hibernate: select nicknames0_.user_id as user_id1_2_0_, nicknames0_.nickname as nickname2_1_0_ from Nicknames nicknames0_ where nicknames0_.user_id=?
Hibernate: select addresses0_.user_id as user_id1_2_0_, addresses0_.fld_street as fld_street2_0_0_ from Addresses addresses0_ where addresses0_.user_id=?
User -- > [alpha, beta] --> [xyz, abc]
Hibernate: delete from Addresses where user_id=?
Hibernate: insert into Addresses (user_id, fld_street) values (?, ?)
Hibernate: delete from Addresses where user_id=?
Hibernate: insert into Addresses (user_id, fld_street) values (?, ?)
If I try to get the list of addresses using user.getAddresses() with in session, then Hibernate deletes & re-inserts records in Addresses table.
Why hibernate tries to delete and re-create records in Addresses table, as this causes performance issue. Also why it is not applicable to basic types like nickNames in my example and not running update commands for the property nickNames?
This behaviour is related to the fact, that there is not bi-directional mapping. To understand that in depth - please read this article:
inverse = “true” example and explanation
And here is the way how to do that with annotation:
inverse=true in JPA annotations
Let me cite from the answer:
I found an answer to this. The mappedBy attribute of #OneToMany annotation behaves the same as inverse = true in the xml file.
Some summary:
Hibernate can issue SQL statements which we would expect. But only, if we do have bidirectional mapping in place.
Then, the other end (address) will be driving the persistence. It will/must know about its parent - and that's why some direct UPDATE statements could be issued.
So, if we want to avoid DELETE and INSERT, we have to use the inverse mapping. Hibernate will issue more "expectable" SQL.
This one is quite old, but might be relevant to sombody. Another tip that helped in my case: if you can separate reads from writes, consider using
#Transactional(readOnly = true)
That makes underlying JPA framework know that no modifications are expected in the current transaction. In practice, Hibernate no longer would run delete + insert when you merely want fetching some data.

what if we do not specify mappedBy attribute in OneToMany annotation?

I am learning JPA with hibernate these days. I am not able to understand why hibernate gives error for a Bidirectional OnetoMany relationship if mappedBy attribute is not specified. Following is the code on which I am getting error:
#Entity
public class Item {
#Id
private long id;
private String name;
private String description;
#OneToMany()
Set<Bid> bids = new HashSet<Bid>();
Bid is the child entity of ITEM
#Entity(name="BIDS")
public class Bid {
#Id
#Column(name="BID_ID")
private Long id;
private Double amount;
#ManyToOne
#JoinColumn(name="ITEM_ID")
Item item;
In the main class I am saving both parent and child entities:
Transaction transaction = session.beginTransaction();
Item item = new Item();
item.setId(111);
item.setDescription("ITEM Description");
item.setName("Name1");
Bid bid = new Bid();
bid.setId(21l);
bid.setAmount(1.1);
bid.setItem(item);
Set<Bid> bids = new HashSet<Bid>();
bids.add(bid);
item.setBids(bids);
session.save(item);
session.save(bid);
transaction.commit();
But hibernate tries to execute following queries and throws excetion that ITEM_BIDS table do not exist.
Hibernate: insert into Item (description, name, id) values (?, ?, ?)
Hibernate: insert into BIDS (amount, ITEM_ID, BID_ID) values (?, ?, ?)
Hibernate: insert into Item_BIDS (Item_id, bids_BID_ID) values (?, ?)
Please tell me why hibernate is generating extra query and how mappedBy element will solve this.
Because if you don't specify mappedBy, you're not saying that the OneToMany between Item and Bid and the ManyToOne between Bid and Item are actually the two sides of a unique bidirectional association.
So Hibernate considers that they are two, different, unidirectional associations. And since the default mapping for a OneToMany association is to use a join table, that's what Hibernate uses.

Categories