Learning Hibernate.
I have the following classes User, Region, Country as follows
public class User {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "first_name")
Private String firstName;
#Column(name = "last_name")
private String lastName;
#OneToOne(fetch = FetchType.EAGER)
#JoinTable(name = "user_country_region", joinColumns ={#JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "country_id") })
private Country userCountry;
#OneToOne(fetch = FetchType.EAGER)
#JoinTable(name = "user_country_region", joinColumns = {#JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "region_id") })
private Region userRegion;
//With its respective Getters and Setters
}
public class Country {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "name")
private String name;
//With its respective Getters and Setters
}
public class Region {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "name")
private String name;
//With its respective Getters and Setters
}
The problem am facing is hibernate query only returns region and not country. What could be causing this?
Tried getting country and region values as below
System.out.println("Country: "+user.getCountry().getName());
System.out.println("Region: "+user.getRegion().getName());
Response from Hibernate Show sql. Seems missing country details.
Hibernate:
select
this_.id as id1_3_3_,
this_.first_name as first_na2_3_3_,
this_.last_name as last_na3_3_3_,
region2_.id as id1_8_2_,
region2_.name as name2_8_2_
from
user this_
left outer join
user_country_region this_1_
on this_.id=this_1_.user_id
left outer join
region region2_
on this_1_.region_id=region2_.id
where
this_.id=?
It is an invalid mapping. I have this error with Hibernate 5 while create the schema by Hibernate.
org.hibernate.boot.spi.InFlightMetadataCollector$DuplicateSecondaryTableException:
Table with that name [user_country_region] already associated with entity
Anyway, if you can use this mapping with your Hibernate version, having such kind of mapping with a join table for two relations is error prone.
Just use this mapping to associate User with Country and Region by foreign key columns.
public class User {
#OneToOne
private Country country;
#OneToOne
private Region region;
}
Related
I have question about access to data.
I have that DB:
[country: id, country_name],
[city: id, country_id, city_name],
[address: id, shop_data_id, city_id, address_data],
[shop_data: id, data]
My relations country-city one to many, city-address one to many, address-shop_data one to one.
I'm looking for information that can I do that SQL query with ORM, or what is the best way do do it in ORM.
UPDATE shop_data
INNER JOIN country ON country.id=1
INNER JOIN city ON country.id=city.country_id
INNER JOIN address ON city.id= address.city_id
INNER JOIN shop_data ON address.shop_data_id=shop_data.id
SET shop_data.data="shop data string"
WHERE shop_data.id=address.shop_data_id
I know that in SQL I should start by shop_data, but by doing this I want to show that I want start in ORM by country entity.
I wrote entities in Hibernate with annotation
#Entity
#Table(name="country")
public class Country{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "country")
private String country;
#OneToMany(mappedBy = "country", fetch=FetchType.LAZY)
#JsonBackReference
private List<City> cities = new ArrayList<>();
// getters/setters ..
}
#Entity
#Table(name="city")
public class City{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "city")
private String city;
#OneToMany(mappedBy = "city", fetch=FetchType.LAZY)
private List<Address> adresses = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="country_id")
#JsonIgnore
#JsonManagedReference
private Country country;
// getters/setters ..
}
#Entity
#Table(name="address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "address")
private String address;
#Column(name = "district")
private String district;
#Column(name = "post_code")
private String postCode;
#OneToOne
#JoinColumn(name="shop_data_id")
private ShopData shopData;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="city_id")
private City city;
// getters/setters ..
}
#Entity
#Table(name="shop_data")
public class shopData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "data")
private String data;
// getters/setters ..
}
I know that I can use getters starting from country that returns List<ObjectType> and from there get my object. Next run function update and update right row. But in this way are done some number of queries.
Is it possible to do by Java Hiberante ORM by one query? Or which way is the best to minimize query amount? By this method I also want to update next also address data.
You should definitely read a book about JPA/Hibernate to understand what JPQL or HQL supports. You can just do joins as you do them with SQL, except for DML statement, but you don't need that. In your case a simple subquery is enough to model what you need. A possible query could look like the following:
UPDATE ShopData s
SET s.data="shop data string"
WHERE EXISTS (
SELECT 1
FROM Country c
WHERE c.id = 1 AND c.city.address.shopData.id = s.id
)
DB Schema - 2 tables (user and role) have many to many relationship and are bridged by an intermediate table (user_role) in postgres. I want to fetch all the roles and the name of a person who created it. Name is available in the users table but all the other details are in roles table. Roles table has a field created_by (User_id of the person who created the role).
I am trying to build a GET Request to view all the roles of a given id with the name of the person who created it
Entity class Users1.java
#Data
#Entity
#Table(name = "user")
public class Users1 {
#Id
#Column(name = "user_id")
private Long user_id;
#Column(name = "org_id")
private Long org_id;
#Column(name = "boss")
private Boolean boss;
#Column(name = "firstname")
private String firstname;
#Column(name = "created_by")
private Long created_by;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_on")
private Date created_on;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {CascadeType.ALL})
#JoinTable(name = "user_role",
joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "created_by")})
private List<Role> roles = new ArrayList<Role>();
// Getters and Setters
Entity class Role.java
#Data
#Entity
#Table(name = "role")
public class Role implements Serializable {
private static final long serialVersionUID = -2343243243242432341L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private Long role_id;
#Column(name = "org_id")
private Long org_id;
#Column(name = "role_name")
private String role_name;
#Column(name = "created_by")
private Long created_by;
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_on")
private Date created_on;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {CascadeType.ALL},
mappedBy = "roles")
private List<Users1> users = new ArrayList<>();
// Getters and Setters
roles.repository class
#Repository
#Transactional
public interface RolesRepository extends CrudRepository<Role,Long> {
#Query(value ="select r.*,u.firstname as firstname
from user u
join user_role ur on u.user_id = ur.user_id
join role r on ur.role_id = r.role_id
where(true = (select u.boss from user u where u.user_id = ?2) and r.org_id = ?1)
or
(false = (select u.boss from user u where u.user_id = ?2) and r.created_by =?2)",
nativeQuery=true)
public Iterable<Role> findAllById(Long org_id,Long created_by );
#Query("from Users1 u where u.user_id=?2 and u.org_id = ?1")
public Users1 findUserbyOrgId(Long org_id, Long created_by);
}
roles.Controller class :
public ResponseEntity<Iterable<Role>> getAllRoles(#RequestParam("org_id") Long org_id,
#RequestParam("created_by") Long created_by ) throws Exception {
if( null != rolesRepository.findUserbyOrg(org_id, created_by)) {
Iterable<Role> Roles = rolesRepository.findAllById(org_id, created_by); }
GET Response from postman:
[
{
"roleId": 3,
"org_id": 2,
"roleName": "manager",
"createdBy": 5,
"createdOn": 1591716178419,
}
]
I'm getting everything except the firstname. I'm not sure how to fetch that in my GET API. Any help would be really appreciated.
Not a direct answer to your question, but #ManyToMany is generally not encouraged in the real field due to the following reasons.
The joining table may need to have more information (but can't)
Since the joining table is hidden, query result is hard to anticipate.
The recommended approach is to
decompose two #ManyToMany classes to two #OneToMany classes +
one #ManyToOne class (joining table)
elevate the joining table to an entity class.
There are many practices of such cases in stackoverflow and youtube. I think it will ultimately save you more time to switch to this approach.
I have a users table, roles table and a notifications table. The user_id is the foreign key for linking users to notifications.
In my users class i am already accessing another table, roles via its foreignkey, role_id.
As shown
#Data
#Entity
#Table(name = "users")
public class User {`enter code here`
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column
#NotBlank
#Size(max = 40)
private String username;
// Valid From
#Column
private Date validFrom;
// Valid To
#Column
private Date validTo;
#Column
#NotBlank
#Size(max = 100)
private String password;
#OneToOne(fetch = FetchType.LAZY)
#JoinTable(name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Role role;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
How can i use the #JoinTable annotation to connect to the notifications table?
Its not accepting duplicates.
You must not will use #JoinTable annotation. The #JoinTable annotation is used only to #ManyToMany relationship.
You need create a new Entity with three field, and each field must has the #ManyToOne and #JoinColumn annotation.
For Instance:
#Entity
#Table(name = "table_name")
class NewEntity {
//Id and anothers fields
#ManyToOne
#JoinColumn(name = "user_id")
private Users users;
#ManyToOne
#JoinColumn(name = "role_id")
private Roles roles;
#ManyToOne
#JoinColumn(name = "notification_id")
private Notifications notifications ;
//getters and setters
}
Greetings to the community,
I am struggling all day to find a solution to the issue below.
The scenario is the following, I have a table
---TABLE_ONE---
INT ID
VARCHAR NAME
PRIMARY_KEY (ID)
and my other table consisted of three columns which consist together a composite key
---TABLE_TWO---
INT TABLE_ONE_ID (FK -> TABLE_ONE.ID)
VARCHAR NAME
VARCHAR EMAIL
PRIMARY_KEY(TABLE_ONE_ID, NAME, EMAIL)
The relationship I want to achieve is that the TABLE_ONE entity will
have a list of objects from the TABLE_TWO (one-to-many relationship).
I tried to do this with as shown below.
#Entity
#Table(name = "TABLE_ONE")
public class TableOne {
#Column(name="id")
private int id;
#Column(name="name")
private String name
#OneToMany(fetch = FetchType.EAGER, mappedBy = "tableOne")
private List<TableTwo> tableTwoList;
//getters, setters, constructors
}
#Entity
#Table(name = "TABLE_TWO")
public class TableTwo {
#EmbeddedId
private TableTwoCompositeId tableTwoCompositeId;
#ManyToOne
#JoinColumn(name = "TABLE_ONE_ID", referencedColumnName = "ID", insertable = false, updatable = false)
private TableOne tableOne;
//getters, setters, constructors
}
#Embeddable
public class TableTwoCompositeId {
#Column(name = "TABLE_ONE_ID")
public Integer provider;
#Column(name = "NAME")
public String name;
#Column(name = "EMAIL")
public String email;
//getters, setters, constructors
}
However, I'm getting javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet and Caused by: java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist when a TableOne object is retrieved from the database.
Thanks in advance for any help!
I think you need several minor changes:
TableOne.id needs an #Id annotation
The type of TableTwoCompositeId.provider should match the type of TableOne.id
TableTwo.tableOne needs a #MapsId annotation to indicate it maps TableTwoCompositeId.provider
Here is how the code should look:
#Entity
#Table(name = "TABLE_ONE")
public class TableOne {
#Id
#Column(name="id")
private int id;
#Column(name="name")
private String name
#OneToMany(fetch = FetchType.EAGER, mappedBy = "tableOne")
private List<TableTwo> tableTwoList;
//getters, setters, constructors
}
#Entity
#Table(name = "TABLE_TWO")
public class TableTwo {
#EmbeddedId
private TableTwoCompositeId tableTwoCompositeId;
#MapsId("provider") // maps provider attribute of embedded id
#ManyToOne
#JoinColumn(name = "TABLE_ONE_ID", referencedColumnName = "ID", insertable = false, updatable = false)
private TableOne tableOne;
//getters, setters, constructors
}
#Embeddable
public class TableTwoCompositeId {
#Column(name = "TABLE_ONE_ID")
public int provider;
#Column(name = "NAME")
public String name;
#Column(name = "EMAIL")
public String email;
//getters, setters, constructors
}
I have two tables which have Many-to-Many relations which have a JoinTable USER_SERVICES as below.
#Entity
public class User implements Serializable {
#NotNull
#Column(unique=true)
private String username;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "USER_SERVICES",
joinColumns = {#JoinColumn(name = "id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "", referencedColumnName = "name")})
private Set<Services> services;
// Getters and Setters
}
#Entity
public class Services implements Serializable {
#NotNull
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long serviceId;
#NotNull
#Column(unique=true)
private String name;
//Getters and Setters
}
The above code creates a table USER_SERVICES, but I also want to have a Many-to-Many relation on the table USER_SERVICES with another table RATINGS which would result in another table USER_SERVICES_RATINGS. how can I define this relation with Hibernate/JPA annotations?
Bi-Directional Many to Many using user managed join table object (Relatively common)
Commonly used when you want to store extra information on the join object such as the date the relationship was created.
public class Foo{
private UUID fooId;
#OneToMany(mappedBy = "bar", cascade=CascadeType.ALL)
private List<FooBar> bars;
}
public class Bar{
private UUID barId;
#OneToMany(mappedBy = "foo", cascade=CascadeType.ALL)
private List<FooBar> foos;
}
#Entity
#Table(name="FOO_BAR")
public class FooBar{
private UUID fooBarId;
#ManyToOne
#JoinColumn(name = "fooId")
private Foo foo;
#ManyToOne
#JoinColumn(name = "barId")
private Bar bar;
//You can store other objects/fields on this table here.
}
You need to create an explicit UserServices entity and setup the relationship to the Ratings entity per your needs.
Remember that in hibernate you model relationships between entities (i.e. your java objects), not db tables.