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
)
Related
In Student class, I am passing the id manually and all my entity-relationship are in LAZY mode.
But because I am passing the id, Spring DATA JPA (Hibernate) will treat as a merge request and make a SELECT call and will try to merge it. But I can not understand why it is trying to JOIN all relationships.
I was expecting select call for student table and not joining other tables. I can not understand why it is happening ?
select
student0_.uuid as uuid1_2_3_,
student0_.address_uuid as address_3_2_3_,
student0_.name as name2_2_3_,
address1_.uuid as uuid1_0_0_,
address1_.city_uuid as city_uui5_0_0_,
address1_.lat as lat2_0_0_,
address1_.lon as lon3_0_0_,
address1_.pincode as pincode4_0_0_,
city2_.uuid as uuid1_1_1_,
city2_.name as name2_1_1_,
subjects3_.student_uuid as student_4_3_5_,
subjects3_.uuid as uuid1_3_5_,
subjects3_.uuid as uuid1_3_2_,
subjects3_.course_start_date as course_s2_3_2_,
subjects3_.name as name3_3_2_,
subjects3_.student_uuid as student_4_3_2_
from
student student0_
inner join
address address1_
on student0_.address_uuid=address1_.uuid
left outer join
city city2_
on address1_.city_uuid=city2_.uuid
left outer join
subject subjects3_
on student0_.uuid=subjects3_.student_uuid
where
student0_.uuid=?
#Entity
public class Student {
#Id
private UUID uuid;
#Column(nullable = false)
private String name;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, optional = false)
private Address address;
#OneToMany(mappedBy = "student", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Subject> subjects;
}
#Entity
public class Subject {
#Id
#GeneratedValue
private UUID uuid;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private ZonedDateTime courseStartDate;
#ManyToOne(fetch = FetchType.EAGER)
private Student student;
}
#Entity
public class City {
#Id
#GeneratedValue
private UUID uuid;
#Column(nullable = false)
private String name;
}
#Entity
public class Address {
#Id
#GeneratedValue
private UUID uuid;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private City city;
#Column(nullable = false)
private String pincode;
#Column
private double lat;
#Column
private double lon;
}
Test Case :
#ExtendWith(SpringExtension.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace = NONE)
public class StudentRepositoryIT {
#PersistenceContext
private EntityManager entityManager;
#Rollback(value = false)
#Test
public void should_fetch_student() {
// GIVEN
UUID uuid = UUID.randomUUID();
Student john = new Student().setName("John Wick").setUuid(uuid);
City newyork = new City().setName("Newyork City");
Address address = new Address().setCity(newyork)
.setLat(12.3)
.setLon(13.4)
.setPincode("010101");
john.setAddress(address);
Subject maths = new Subject().setName("maths").setCourseStartDate(ZonedDateTime.now()).setStudent(john);
Subject english = new Subject().setName("english").setCourseStartDate(ZonedDateTime.now()).setStudent(john);
Subject hindi = new Subject().setName("hindi").setCourseStartDate(ZonedDateTime.now()).setStudent(john);
Subject geology = new Subject().setName("geology").setCourseStartDate(ZonedDateTime.now()).setStudent(john);
Subject physics = new Subject().setName("physics").setCourseStartDate(ZonedDateTime.now()).setStudent(john);
Subject science = new Subject().setName("science").setCourseStartDate(ZonedDateTime.now()).setStudent(john);
john.setSubjects(List.of(maths, english, hindi, geology, physics, science));
studentRepository.saveAndFlush(john);
TestTransaction.end();
// WHEN
TestTransaction.start();
Student student = entityManager.find(Student.class, uuid);
System.out.println(student);
System.out.println(student.getAddress());
Address address1 = student.getAddress();
System.out.println(address1.getLat());
// THEN
check query console
}
}
Entity Relationship diagram
It does so because you are using CascadeType.ALL which means that Hibernate should cascade also for the merge operation. In order to cascade, Hibernate must load the data first and instead of loading the data by doing individual selects, Hibernate uses joins.
I'm not sure about City, but for the others Hibernate needs the join to determine if the respective field is null or not, which it needs to know before returning your root entity.
Could you please advise how to write query in Spring Data JPA repository? Here is the situation:
I have 2 Entities: Customer and Product with relationship 'OneToMany' - means one Customer may have many products. In code it looks like Customer entity has Set products and Product has reference to Customer customer, very simple. If I retrieve Customer from DB JSON would look like this: {"id":10, "name":'John Smith',"personalCode":12345678,"products":[ {"id":15,"type":"productType1"}, {"id":20,"type":"productType2"}] }
The question is how can I write query to DB to find all customers whose products match passed products collection? For example I want to find all customers who owns products with type1 and type2. THANKS!
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
private String personalCode;
#Enumerated(EnumType.STRING)
private Country country;
private String internetBankUserId;
#Enumerated(EnumType.STRING)
private CustomerType type;
#JsonManagedReference
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
#SortNatural
private SortedSet<Product> products = new TreeSet<>();
#Entity
#Table(name = "product")
public class Product implements Comparable<Product>{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private ProductType type;
#JsonBackReference
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id")
private Customer customer;
This solution worked for me:
#Query("SELECT c FROM Customer c join c.products p where p.type in :products
and SIZE(c.products) >= :count")
Set<Customer> findAllByProductType (#Param("products")Set<ProductType> products,
#Param("count") Integer count );
So basically I have two tables : NaturalPerson which holds personalNumber column and NaturalPersonReserve where I added a new column personalNumber. Both tables have existing data in it and I want to populate my NaturalPersonReserve's new Column personalNumber from naturalPerson
s table ( I mean the existing data to update from One tables's column to second)
NaturalPerson Entity :
#Entity
#Table(name = "naturalperson")
public class NaturalPerson implements Serializable {
#Id
#Column
#GeneratedValue(strategy = GenerationType.AUTO)
private int personId;
#Column(unique = true)
private String personalNumber;
#Column
private String serialNumber;
#Column
private String firstname;
#Column
private String lastname;
#Column
private String birthdate;
#Column
private String gender;
NaturalPersonReserve Entity :
#Entity
#Table(name = "natural_person_reserve")
public class NaturalPersonReserve extends SuperModel{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
#JoinColumn(name = "payment_id")
private PaymentParts payment;
// The relationship
#ManyToOne
#JoinColumn(name = "person_id")
private NaturalPerson person;
#ManyToOne
#JoinColumn(name = "company_id")
private Company company;
#Column(name = "amount", columnDefinition = "DECIMAL(10,2) DEFAULT 0.0")
private double amount;
#Enumerated(EnumType.STRING)
#Column(name = "operation_type")
private EReserveType operationType;
// My added column
#Column(unique = true)
private String personalNumber;
Basically you added a new column, and you want to populate it with data from another table. That's not really a java/spring/hibernate issue. That kind of problem will exist regardless of how your application is built.
I can only see two ways:
Write a SQL statement that will update the table based on a select statement. The syntax may change depending on which DB you use. This post has an Oracle example.
Write a program (in your case probably in Java) that will update each register with the correct data. It will have the same effect as the above option, but it will be implemented in your language of choice.
I am new to Hibernate and I am trying to figure out how to query a many to many relationship mapped as an entity due to the need of an extra column.
In particular, following the example I found at codejava.net (http://www.codejava.net/frameworks/hibernate/hibernate-many-to-many-association-with-extra-columns-in-join-table-example) I mapped the relation like this:
Student.java
#Entity
public class Student implements Serializable {
#Id
#Column
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String surname;
// Constructor, getters, setters, hashcode, equals
}
Course.java
#Entity
#Table(
uniqueConstraints = #UniqueConstraint(
columnNames {"name","year"})
)
public class Course implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private long id;
#Column
private String name;
#Column
private String year;
#OneToMany(mappedBy = "course")
private Set<Student_Course> students = new LinkedHashSet<>();
// Constructor, getters, setters, hashcode, equals
}
Student_Course.java
#Entity
public class Student_Course implements Serializable {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column
private long id;
#ManyToOne
#JoinColumn(name = "student_email")
private Student student;
#ManyToOne
#JoinColumn(name = "course_id")
private Course course;
#Column(nullable = false,
columnDefinition = "int default 0")
private int score;
// Constructor, getters, setters, hashcode, equals
}
Now what I want to achieve is to find out, with an hql query, the names and surnames of students enrolled in a given course (I know the name and the year of the course).
I know this is probably easy, but I can't produce a working query in HQL.
Thank you in advance.
I believe that this query can do what you want:
SELECT sc.student.name, sc.student.surname
FROM Course c JOIN c.students sc
WHERE c.name = :name AND c.year = :year
Select name, surname from Student where email in
(Select student.email from Student_Course
where Student_course.course.name=:courseName and Student_course.course.year= :year)
and then set both courseName and year
Update
Select student.name,student.surname from Student_Course
where Student_course.course.name=:courseName and Student_course.course.year= :year
I'm using hibernate-core:3.3.1.GA
I have three mappings:
class PlayerAccount:
#Entity
#Table(name = "player_account")
public class PlayerAccount {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(targetEntity = Player.class, fetch = FetchType.EAGER)
#JoinColumn(name="player_id")
private Player player;
//GET, SET
}
class PlayerAchievements:
#Entity
#Table(name="player_achievement")
public class PlayerAchievement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(targetEntity = Player.class, fetch = FetchType.EAGER)
#JoinColumn(name = "player_id")
private Player player;
//GET, SET
}
and class Player
#Entity
#Table(name = "player")
#Inheritance(strategy = InheritanceType.JOINED)
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "ps_id")
private String psId;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
//GET, SET
}
Issue:
When I try to write a simple criteria query like the following:
Criteria criteria = getSession().createCriteria(PlayerAccount.class);
criteria.add(Restrictions.eq("playerId", playerId));
criteria.list();
The resulting sql-query contains joins have the following view:
SELECT
-- columns to select
FROM player_account this_
LEFT OUTER JOIN player player2_
ON this_.player_id=player2_.id
LEFT OUTER JOIN external_partner_player player2_1_
ON player2_.id=player2_1_.player_id
LEFT OUTER JOIN player_achievement achievemen3_
ON player2_.id=achievemen3_.player_id
WHERE player_id = 2362189
The thing is player_achievements may contain more than one row with the same player_id value. Since we probably have a duplicated result in that criteria query. How to fix that using hibernate whithout the writing an sql-query?
Because the Fetch Types are eager. That means that you always want the entities loaded when you load the main entity.
You can use FetchType.LAZY for best perform. Here explains better: https://howtoprogramwithjava.com/hibernate-eager-vs-lazy-fetch-type/
You need to do SELECT DISTINCT, like this:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);