Hướng dẫn JPA

Created

Oct 28, 2021 6:47 AM

Tags

spring bootmiddle

Chapter

  • Giới thiệu

  • Tạo project

  • Tạo Table

  • Chạy thử

  • Thêm dữ liệu

Giới thiệu

Cách biểu thị quan hệ n-n trong cơ sở dữ liệu là rất phổ biến, ví dụ một địa chỉ có thể có nhiều người ở [gia đình]. và một người có thể có nhiều hơn một địa chỉ.

Bình thường, khi các bạn tạo table trong csdl để biểu thị mối quan hệ này, chúng ta sẽ tạo ra một bảng mới, tham chiếu tới cả bảng này.

Thể hiện mỗi quan hệ này một cách đầy đủ trongcodebằngHibernatethì chúng ta sẽ dùng@ManyToMany

Trong bài sử dụng các kiến thức:

  1. Hibernate là gì?
  2. Cách sử dụng Lombok để tiết kiệm thời gian code

Tạo project

Toàn bộ bài viết được up tạiGithub:github.com/loda-kun/java-all

Chúng ta sẽ sử dụngGradleđể tạo một project có khai báoSpring BootJpađể hỗ trợ cho việc demo@ManyToMany.

Các bạn có thể tự tạo 1 project Spring-boot với gradle đơn giản tại://start.spring.io

plugins { id 'org.springframework.boot' version '2.1.4.RELEASE' id 'java' } apply plugin: 'io.spring.dependency-management' group 'me.loda.java' version '1.0-SNAPSHOT' sourceCompatibility = 1.8 configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral[] } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' }

Trong ứng dụng trên bạn sẽ thấy cócom.h2database:h2. Đây là mộtdatabase, tuy nhiên nó chỉ tồn tại trong bộ nhớ. Tức làm mỗi khi chạy chương trình này, nó sẽ tạo database trongRAM, và tắt chương trình đi nó sẽ mất.

Chúng ta sẽ sử dụngH2thay choMySqlđể cho.. tiện!

Khi tạo xong project, sẽ có thư mục như sau:

Tạo Table

Để tạo table, chúng ta tạo ra cácClasstương ứng.

import java.util.Collection; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; @Entity // Đánh dấu đây là table trong db @Data // lombok giúp generate các hàm constructor, get, set v.v. @AllArgsConstructor @NoArgsConstructor @Builder public class Address { @Id //Đánh dấu là primary key @GeneratedValue // Giúp tự động tăng private Long id; private String city; private String province; @ManyToMany[cascade = CascadeType.ALL, fetch = FetchType.LAZY] // Quan hệ n-n với đối tượng ở dưới [Person] [1 địa điểm có nhiều người ở] @EqualsAndHashCode.Exclude // không sử dụng trường này trong equals và hashcode @ToString.Exclude // Khoonhg sử dụng trong toString[] @JoinTable[name = "address_person", //Tạo ra một join Table tên là "address_person" joinColumns = @JoinColumn[name = "address_id"], // TRong đó, khóa ngoại chính là address_id trỏ tới class hiện tại [Address] inverseJoinColumns = @JoinColumn[name = "person_id"] //Khóa ngoại thứ 2 trỏ tới thuộc tính ở dưới [Person] ] private Collection persons; }

@Entity @Data @NoArgsConstructor @AllArgsConstructor @Builder public class Person { @Id @GeneratedValue private Long id; private String name; // mappedBy trỏ tới tên biến persons ở trong Address. @ManyToMany[mappedBy = "persons"] // LAZY để tránh việc truy xuất dữ liệu không cần thiết. Lúc nào cần thì mới query @EqualsAndHashCode.Exclude @Exclude private Collection addresses; }

Nếu chúng ta chưa tạo ra các table trong cơ sở dữ liệu, thì mặc địnhHibernatesẽ bind dữ liệu từ class xuống và tạo table cho chúng ta.

Bạn phải tạo file configsrc\main\resources\application.propertiesnhư sau để kết nối tớiH2database nhé:

spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= // Không có password, vào thẳng luôn spring.jpa.database-platform=org.hibernate.dialect.H2Dialect # Cho phép vào xem db thông qua web spring.h2.console.enabled=true

Chạy thử

Bạn tạo fileManyToManyExampleApplicationvà cấu hìnhSpring Bootvà khởi chạy chương trình.

@SpringBootApplication @RequiredArgsConstructor public class ManyToManyExampleApplication { public static void main[String[] args] { SpringApplication.run[ManyToManyExampleApplication.class, args]; } }

Sau khi chạy xong, hãy truy cập vào//localhost:8080/h2-console/để vào xem database có gì nhé.

Bạn sẽ thấy nó tạo table giống với mô tả ở đầu bài. Gồm có hai bảng chính làaddressperson. Ngoài ra, sẽ tạo ra một bảng trung gian ở giữa liên kết hai bảng làaddress_person.

Thêm dữ liệu

Để thêm dữ liệu vào database, chúng ta sẽ dùng tớiSpring JPA.

import org.springframework.data.jpa.repository.JpaRepository; public interface AddressRepository extends JpaRepository { } public interface PersonRepository extends JpaRepository { }

Chúng ta sẽ tạo một chương trìnhSpring Bootđơn giản bằng cách sử dụngCommandLineRunnerđể chạy code ngay khi khởi động.

import javax.transaction.Transactional; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; @SpringBootApplication @RequiredArgsConstructor public class ManyToManyExampleApplication implements CommandLineRunner { public static void main[String[] args] { SpringApplication.run[ManyToManyExampleApplication.class, args]; } // Sử dụng @RequiredArgsConstructor và final để thay cho @Autowired private final PersonRepository personRepository; private final AddressRepository addressRepository; @Override @Transactional public void run[String... args] throws Exception { // Tạo ra đối tượng Address Address hanoi = Address.builder[] .city["hanoi"] .build[]; Address hatay = Address.builder[] .city["hatay"] .build[]; // Tạo ra đối tượng person Person person1 = Person.builder[] .name["loda1"] .build[]; Person person2 = Person.builder[] .name["loda2"] .build[]; // set Persons vào address hanoi.setPersons[Lists.newArrayList[person1, person2]]; hatay.setPersons[Lists.newArrayList[person1]]; // Lưu vào db // Chúng ta chỉ cần lưu address, vì cascade = CascadeType.ALL nên nó sẽ lưu luôn Person. addressRepository.saveAndFlush[hanoi]; addressRepository.saveAndFlush[hatay]; // Vào://localhost:8080/h2-console/ để xem dữ liệu đã insert Address queryResult = addressRepository.findById[1L].get[]; System.out.println[queryResult.getCity[]]; System.out.println[queryResult.getPersons[]]; } } // Output: // hanoi // [Person[id=2, name=loda1], Person[id=3, name=loda2]]

Lưu ý ở đây chúng ta dùng@Transactional. Đê khiến toàn bộ code chạy trong hàm đều nằm trongSessionquản lý củaHibernate.

Nếu không có@Transactionalthì việc bạn gọiaddress.getPersons[]sẽ bị lỗi, vì nó không thể query xuống database để lấy dữ liệu person lên được. Bạn ghi nhớ chỗ này nhé.

Kết quả trong database lúc này:

Address

Person

Address_Person

Bài viết của mình không còn gì để ngắn hơn được nữa :[[[ thật hổ thẹn, mình có up code lên đây, bạn chạy code cái là hiểu liền à:

github.com/loda-kun/java-all

Chúc các bạn học tập thật tốt! ahuu

  1. 🪂

    「Jpa」Hướng dẫn sử dụng @OneToOne

  2. 「Jpa」Hướng dẫn @OneToMany và @ManyToOne

Nếu có, toàn bộ project / code mẫu được lưu trữ tại GitHub

Đây là một bài viết trong

[SB0] Series làm chủ Spring Boot - Zero to Hero

Nếu bạn phát hiện bài viết có lỗi hoặc outdated, hãy báo lại giúp mình theo email: hoặc qua Nam Hoàng Nguyễn [facebook.com]

Video liên quan

Chủ Đề