Helpex - Trao đổi & giúp đỡ Đăng nhập

Giới thiệu về JPA bằng cách sử dụng dữ liệu khởi động mùa xuân

Hướng dẫn này sẽ giúp bạn hiểu JPA là gì và thiết lập một ví dụ JPA đơn giản bằng Spring Boot.

Bạn sẽ học :

  • JPA là gì?
  • Vấn đề được giải quyết bởi JPA là gì?
  • Các lựa chọn thay thế cho JPA là gì?
  • Hibernate là gì và nó liên quan như thế nào đến JPA?
  • Spring Data JPA là gì?
  • Cách tạo một dự án JPA đơn giản bằng cách sử dụng Spring Boot Data JPA Starter.

Các công cụ bạn sẽ cần :

  • Maven 3.0+ (công cụ xây dựng)
  • IDE yêu thích của bạn; chúng tôi sẽ sử dụng Eclipse.
  • JDK 1.8+
  • Trong cơ sở dữ liệu bộ nhớ H2

Đối tượng Không khớp trở kháng quan hệ là gì?

Java là một ngôn ngữ lập trình hướng đối tượng. Trong Java, tất cả dữ liệu được lưu trữ trong các đối tượng.

Thông thường, cơ sở dữ liệu quan hệ được sử dụng để lưu trữ dữ liệu (ngày nay, một số kho dữ liệu NoSQL khác cũng đang trở nên phổ biến, nhưng chúng tôi sẽ tránh xa chúng ngay bây giờ). Cơ sở dữ liệu quan hệ lưu trữ dữ liệu trong các bảng.

Cách chúng ta thiết kế các đối tượng khác với cách thiết kế cơ sở dữ liệu quan hệ. Điều này dẫn đến một trở kháng không phù hợp.

  • Lập trình hướng đối tượng bao gồm các khái niệm như đóng gói, kế thừa, giao diện và đa hình.
  • Cơ sở dữ liệu quan hệ được tạo thành từ các bảng với các khái niệm như chuẩn hóa.

Ví dụ về sự không khớp trở kháng quan hệ đối tượng

Hãy xem xét một ví dụ đơn giản với nhân viên và nhiệm vụ.

Mỗi nhân viên có thể có nhiều nhiệm vụ. Mỗi nhiệm vụ có thể được chia sẻ bởi nhiều nhân viên. Có một mối quan hệ nhiều-nhiều giữa chúng. Hãy xem xét một vài ví dụ về sự không phù hợp trở kháng.

ví dụ 1

Bảng nhiệm vụ dưới đây được ánh xạ tới bảng nhiệm vụ. Tuy nhiên, có sự không khớp trong tên cột.

public class Task {
 private int id;
 private String desc;
 private Date targetDate;
 private boolean isDone;
 private List < Employee > employees;
}
CREATE TABLE task 
  ( 
     id          INTEGER GENERATED BY DEFAULT AS IDENTITY, 
     description VARCHAR(255), 
     is_done     BOOLEAN, 
     target_date TIMESTAMP, 
     PRIMARY KEY (id) 
  ) 

Ví dụ 2

Mối quan hệ giữa các đối tượng được thể hiện theo một cách khác với mối quan hệ giữa các bảng.

Mỗi nhân viên có thể có nhiều nhiệm vụ. Mỗi nhiệm vụ có thể được chia sẻ bởi nhiều nhân viên. Có một mối quan hệ nhiều-nhiều giữa chúng.

public class Employee {

 //Some other code

 private List < Task > tasks;
}

public class Task {

 //Some other code

 private List < Employee > employees;
}
CREATE TABLE employee 
  ( 
     id            BIGINT NOT NULL, 
     OTHER_COLUMNS
  ) 


  CREATE TABLE employee_tasks 
  ( 
     employees_id BIGINT NOT NULL, 
     tasks_id     INTEGER NOT NULL 
  ) 

  CREATE TABLE task 
  ( 
     id          INTEGER GENERATED BY DEFAULT AS IDENTITY, 
     OTHER_COLUMNS
  ) 

Ví dụ 3 

Đôi khi, nhiều lớp được ánh xạ vào một bảng và ngược lại.

Các đối tượng:

public class Employee {
 //Other Employee Attributes
}

public class FullTimeEmployee extends Employee {
 protected Integer salary;
}

public class PartTimeEmployee extends Employee {
 protected Float hourlyWage;
}

Những cái bàn:

CREATE TABLE employee 
  ( 
     employee_type VARCHAR(31) NOT NULL, 
     id            BIGINT NOT NULL, 
     city          VARCHAR(255), 
     state         VARCHAR(255), 
     street        VARCHAR(255), 
     zip           VARCHAR(255), 
     hourly_wage   FLOAT,--PartTimeEmployee 
     salary        INTEGER, --FullTimeEmployee 
     PRIMARY KEY (id) 
  ) 

Các phương pháp tiếp cận trước JPA: JDBC, Spring JDBC và MyBatis

Các cách tiếp cận khác trước JPA tập trung vào truy vấn và cách dịch kết quả từ truy vấn sang đối tượng. Bất kỳ cách tiếp cận nào sử dụng truy vấn thường thực hiện hai điều:

  1. Đặt tham số cho truy vấn . Chúng ta cần đọc các giá trị từ các đối tượng và đặt chúng làm tham số cho truy vấn.
  2. Thanh lý các kết quả từ truy vấn . Các kết quả từ truy vấn cần được ánh xạ tới các bean.

JDBC

  • JDBC là viết tắt của kết nối cơ sở dữ liệu Java.
  • Nó sử dụng các khái niệm như Statement, PreparedStatement và ResultSet
  • Trong ví dụ dưới đây, truy vấn được sử dụng là:  Update todo set user=?, desc=?, target_date=?, is_done=? where id=?
  • Các giá trị cần thiết để thực hiện truy vấn được đặt vào truy vấn bằng cách sử dụng các phương pháp đặt khác nhau trên PreparedStatement.
  • Kết quả từ truy vấn được điền vào Tập kết quả. Chúng tôi phải viết mã để thanh lý ResultSet thành các đối tượng.

Cập nhật việc cần làm:

Connection connection = datasource.getConnection();

PreparedStatement st = connection.prepareStatement(
 "Update todo set user=?, desc=?, target_date=?, is_done=? where id=?");

st.setString(1, todo.getUser());
st.setString(2, todo.getDesc());
st.setTimestamp(3, new Timestamp(
 todo.getTargetDate().getTime()));
st.setBoolean(4, todo.isDone());
st.setInt(5, todo.getId());

st.execute();

st.close();

connection.close();

Lấy một việc cần làm:

Connection connection = datasource.getConnection();

PreparedStatement st = connection.prepareStatement(
 "SELECT * FROM TODO where id=?");

st.setInt(1, id);

ResultSet resultSet = st.executeQuery();


if (resultSet.next()) {

 Todo todo = new Todo();
 todo.setId(resultSet.getInt("id"));
 todo.setUser(resultSet.getString("user"));
 todo.setDesc(resultSet.getString("desc"));
 todo.setTargetDate(resultSet.getTimestamp("target_date"));
 return todo;
}

st.close();

connection.close();

return null;

Mùa xuân JDBC

  • Spring JDBC cung cấp một lớp trên đầu JDBC.
  • Nó đã sử dụng các khái niệm như JDBCTemplate.
  • Thông thường cần số dòng ít hơn so với JDBC vì sau đây được đơn giản hóa.
    • Ánh xạ các tham số cho các truy vấn.
    • Thanh lý Kết quả Đặt thành đậu.

Cập nhật việc cần làm:

jdbcTemplate
 .update("Update todo set user=?, desc=?, target_date=?, is_done=? where id=?",
  todo.getUser(),
  todo.getDesc(),
  new Timestamp(todo.getTargetDate().getTime()),
  todo.isDone(),
  todo.getId());

Lấy một việc cần làm:

@Override
public Todo retrieveTodo(int id) {

 return jdbcTemplate.queryForObject(
  "SELECT * FROM TODO where id=?",
  new Object[] {
   id
  }, new TodoMapper());

}

Trình ánh xạ hàng có thể sử dụng lại:

// new BeanPropertyRowMapper(TodoMapper.class)
class TodoMapper implements RowMapper < Todo > {
 @Override
 public Todo mapRow(ResultSet rs, int rowNum)
 throws SQLException {
  Todo todo = new Todo();

  todo.setId(rs.getInt("id"));
  todo.setUser(rs.getString("user"));
  todo.setDesc(rs.getString("desc"));
  todo.setTargetDate(
   rs.getTimestamp("target_date"));
  todo.setDone(rs.getBoolean("is_done"));
  return todo;
 }
}

MyBatis

MyBatis loại bỏ nhu cầu viết mã theo cách thủ công để thiết lập các tham số và truy xuất kết quả. Nó cung cấp cấu hình dựa trên XML hoặc Chú thích đơn giản để ánh xạ các POJO của Java với cơ sở dữ liệu.

Chúng tôi so sánh các cách tiếp cận được sử dụng để viết các truy vấn dưới đây:

  • JDBC hoặc Spring JDBC:
    • Update todo set user=?, desc=?, target_date=?, is_done=? where id=?
  • MyBatis: 
    • Update todo set user=#{user}, desc=#{desc}, target_date=#{targetDate}, is_done=#{isDone} where id=#{id}

Cập nhật việc cần làm và truy xuất việc cần làm:

@Mapper
public interface TodoMybatisService
extends TodoDataService {

 @Override
 @Update("Update todo set user=#{user}, desc=#{desc}, target_date=#{targetDate}, is_done=#{isDone} where id=#{id}")
 public void updateTodo(Todo todo) throws SQLException;

 @Override
 @Select("SELECT * FROM TODO WHERE id = #{id}")
 public Todo retrieveTodo(int id) throws SQLException;
}

public class Todo {

 private int id;

 private String user;

 private String desc;

 private Date targetDate;

 private boolean isDone;
}

Các tính năng chung của JDBC, Spring JDBC và MyBatis

  • JDBC, Spring JDBC và MyBatis liên quan đến việc viết các truy vấn.
  • Trong ứng dụng lớn, các truy vấn có thể trở nên phức tạp. Đặc biệt là khi chúng ta lấy dữ liệu từ nhiều bảng.
  • Điều này tạo ra một vấn đề bất cứ khi nào có thay đổi trong cấu trúc của cơ sở dữ liệu.

JPA hoạt động như thế nào?

JPA phát triển là kết quả của một quá trình suy nghĩ khác biệt. Làm thế nào về ánh xạ các đối tượng trực tiếp vào bảng?

  • Thực thể
  • Thuộc tính
  • Các mối quan hệ

Ánh xạ này còn được gọi là ORM (Object Relational Mapping). Trước JPA, ORM là thuật ngữ được sử dụng phổ biến hơn để chỉ các khuôn khổ này. Đó là một trong những lý do, Hibernate được gọi là khuôn khổ ORM.

Các khái niệm quan trọng trong JPA

Giới thiệu về JPA bằng cách sử dụng dữ liệu khởi động mùa xuân

JPA cho phép ánh xạ các lớp ứng dụng vào các bảng trong cơ sở dữ liệu.

  • Trình quản lý thực thể : Sau khi các ánh xạ được xác định, Trình quản lý thực thể có thể quản lý các thực thể của bạn. Trình quản lý thực thể xử lý tất cả các tương tác với cơ sở dữ liệu.
  • JPQL (Java Persistence Query Language) : Cung cấp các cách viết truy vấn để thực hiện tìm kiếm đối với các thực thể. Một điều quan trọng cần hiểu là chúng khác với các truy vấn SQL. Các truy vấn JPQL đã hiểu các ánh xạ được xác định giữa các thực thể. Chúng tôi có thể thêm các điều kiện bổ sung nếu cần.
  • API Criteria : Xác định một API dựa trên Java để thực hiện các tìm kiếm dựa trên cơ sở dữ liệu.

JPA so với Hibernate

Hibernate là một trong những khung ORM phổ biến nhất.

JPA xác định đặc điểm kỹ thuật. Nó là một API.

  • Làm thế nào để bạn xác định các thực thể?
  • Làm thế nào để bạn lập bản đồ các thuộc tính?
  • Làm thế nào để bạn lập bản đồ mối quan hệ giữa các thực thể?
  • Ai quản lý các thực thể?

Hibernate là một trong những cách triển khai phổ biến của JPA.

  • Hibernate hiểu các ánh xạ mà chúng ta thêm vào giữa các đối tượng và bảng. Nó đảm bảo rằng dữ liệu được lưu trữ / truy xuất từ ​​cơ sở dữ liệu dựa trên các ánh xạ.
  • Hibernate cũng cung cấp các tính năng bổ sung trên JPA. Nhưng tùy thuộc vào chúng sẽ có nghĩa là khóa chế độ Ngủ đông. Bạn không thể chuyển sang các triển khai JPA khác như Toplink.

Ví dụ về Ánh xạ JPA

Hãy xem một vài ví dụ để hiểu cách JPA có thể được sử dụng để ánh xạ các đối tượng vào bảng.

ví dụ 1

Bảng nhiệm vụ dưới đây được ánh xạ tới bảng nhiệm vụ. Tuy nhiên, có sự không khớp trong tên cột. Chúng tôi sử dụng một vài chú thích JPA để thực hiện ánh xạ:

  • @Table(name = “Task”) 
  • @Id 
  • @GeneratedValue 
  • @Column(name = “description”) 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "Task")
public class Task {
 @Id
 @GeneratedValue
 private int id;

 @Column(name = "description")
 private String desc;

 @Column(name = "target_date")
 private Date targetDate;

 @Column(name = "is_done")
 private boolean isDone;

}
CREATE TABLE task 
  ( 
     id          INTEGER GENERATED BY DEFAULT AS IDENTITY, 
     description VARCHAR(255), 
     is_done     BOOLEAN, 
     target_date TIMESTAMP, 
     PRIMARY KEY (id) 
  ) 

Ví dụ 2

Mối quan hệ giữa các đối tượng được thể hiện theo một cách khác so với mối quan hệ giữa các bảng.

Mỗi nhân viên có thể có nhiều nhiệm vụ. Mỗi nhiệm vụ có thể được chia sẻ bởi nhiều nhân viên. Có một mối quan hệ nhiều-nhiều giữa chúng. Chúng tôi sử dụng @ManyToMany chú thích để thiết lập mối quan hệ.

public class Employee {

     //Some other code

@ManyToMany
private List<Task> tasks;
}

public class Task {

     //Some other code

@ManyToMany(mappedBy = "tasks")
private List<Employee> employees;
}
CREATE TABLE employee 
  ( 
     id            BIGINT NOT NULL, 
     OTHER_COLUMNS
  ) 


  CREATE TABLE employee_tasks 
  ( 
     employees_id BIGINT NOT NULL, 
     tasks_id     INTEGER NOT NULL 
  ) 

  CREATE TABLE task 
  ( 
     id          INTEGER GENERATED BY DEFAULT AS IDENTITY, 
     OTHER_COLUMNS
  ) 

Ví dụ 3

Đôi khi, nhiều lớp được ánh xạ vào một bảng và ngược lại. Trong những tình huống này, chúng tôi xác định một chiến lược kế thừa. Trong ví dụ này, chúng tôi sử dụng một chiến lược được gọi là   InheritanceType.SINGLE_TABLE.

Các đối tượng:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "EMPLOYEE_TYPE")
public class Employee {
 //Other Employee Attributes
}

public class FullTimeEmployee extends Employee {
 protected Integer salary;
}

public class PartTimeEmployee extends Employee {
 protected Float hourlyWage;
}

Những cái bàn:

CREATE TABLE employee 
  ( 
     employee_type VARCHAR(31) NOT NULL, 
     id            BIGINT NOT NULL, 
     city          VARCHAR(255), 
     state         VARCHAR(255), 
     street        VARCHAR(255), 
     zip           VARCHAR(255), 
     hourly_wage   FLOAT,--PartTimeEmployee 
     salary        INTEGER, --FullTimeEmployee 
     PRIMARY KEY (id) 
  ) 

Ví dụ về mã từng bước

Khởi động ứng dụng Web với Spring Initializr

Tạo một ứng dụng JPA với Spring Initializr rất đơn giản.

Giới thiệu về JPA bằng cách sử dụng dữ liệu khởi động mùa xuân

Như thể hiện trong hình trên, các bước sau phải được thực hiện:

  • Khởi chạy Spring Initializr  và chọn tùy chọn sau
    • Chọn com.in28minutes.springbootlàm nhóm
    • Chọn H2InMemoryDbDemolàm phần mềm
    • Chọn các phần phụ thuộc sau
      • Web
      • JPA
      • H2 (chúng tôi sử dụng H2 làm cơ sở dữ liệu trong bộ nhớ của mình)
  • Nhấp vào Tạo dự án  ở cuối trang.
  • Nhập dự án vào Eclipse.

Cấu trúc của dự án được tạo

  • H2InMemoryDbDemoApplication.java: Trình khởi chạy Spring Boot; khởi tạo cấu hình tự động Spring Boot và ngữ cảnh ứng dụng Spring.
  • application.properties: Tệp cấu hình ứng dụng.
  • H2InMemoryDbDemoApplicationTests.java: Trình khởi chạy đơn giản để sử dụng trong các bài kiểm tra đơn vị.
  • pom.xml: Bao gồm các phần phụ thuộc cho web khởi động Spring Boot và dữ liệu JPA. Sử dụng cha mẹ khởi động Spring Boot làm pom mẹ.

Các phần phụ thuộc quan trọng được hiển thị bên dưới:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
	<scope>runtime</scope>
</dependency>

Thực thể Người dùng

Hãy xác định Người dùng bean và thêm các chú thích JPA thích hợp.

package com.example.h2.user;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

@Entity
@NamedQuery(query = "select u from User u", name = "query_find_all_users")
public class User {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;

 private String name; // Not perfect!! Should be a proper object!

 private String role; // Not perfect!! An enum should be a better choice!

 protected User() {}

 public User(String name, String role) {
  super();
  this.name = name;
  this.role = role;
 }

}

Những điều quan trọng cần lưu ý:

  • @Entity: Chỉ định rằng lớp là một thực thể. Chú thích này được áp dụng cho lớp thực thể.
  • @NamedQuery: Chỉ định một truy vấn tĩnh, được đặt tên trong ngôn ngữ truy vấn bền vững Java.
  • @Id: Chỉ định khóa chính của một thực thể.
  • @GeneratedValue: Cung cấp đặc điểm kỹ thuật của các chiến lược tạo cho các giá trị của khóa chính.
  • protected User(): Hàm tạo mặc định để làm cho JPA hài lòng.

Dịch vụ người dùng để nói chuyện với người quản lý thực thể

Thông thường, với JPA, chúng ta cần tạo một dịch vụ để nói chuyện với người quản lý thực thể. Trong ví dụ này, chúng tôi tạo một UserService để quản lý sự tồn tại của thực thể người dùng.

@Repository
@Transactional
public class UserService {

 @PersistenceContext
 private EntityManager entityManager;

 public long insert(User user) {
  entityManager.persist(user);
  return user.getId();
 }

 public User find(long id) {
  return entityManager.find(User.class, id);
 }

 public List < User > findAll() {
  Query query = entityManager.createNamedQuery(
   "query_find_all_users", User.class);
  return query.getResultList();
 }
}

Những điều quan trọng cần lưu ý

  • @Repository: Spring Annotation để chỉ ra rằng thành phần này xử lý việc lưu trữ dữ liệu vào kho dữ liệu.
  • @Transactional: Chú thích mùa xuân được sử dụng để đơn giản hóa việc quản lý giao dịch
  • @PersistenceContext: Một ngữ cảnh liên tục xử lý một tập hợp các thực thể giữ dữ liệu được lưu giữ lâu dài trong một số lưu trữ liên tục (ví dụ: cơ sở dữ liệu). Đặc biệt, ngữ cảnh nhận thức được các trạng thái khác nhau mà một thực thể có thể có (ví dụ: được quản lý, tách rời) liên quan đến cả ngữ cảnh và kho lưu trữ liên tục cơ bản.
  • EntityManager : Giao diện được sử dụng để tương tác với ngữ cảnh liên tục.
  • entityManager.persist(user): Làm cho cá thể thực thể người dùng được quản lý và liên tục tức là được lưu vào cơ sở dữ liệu.
  • entityManager.createNamedQuery: Tạo một phiên bản TypedQuery để thực thi một ngôn ngữ truy vấn Java Persistence có tên là truy vấn. Tham số thứ hai cho biết loại kết quả.

Ghi chú từ tài liệu này :

Một cá thể EntityManager được liên kết với một ngữ cảnh liên tục. Bối cảnh tồn tại là một tập hợp các cá thể thực thể trong đó đối với bất kỳ danh tính thực thể liên tục nào đều có một cá thể thực thể duy nhất. Trong bối cảnh tồn tại, các cá thể thực thể và vòng đời của chúng được quản lý. API EntityManager được sử dụng để tạo và xóa các cá thể thực thể liên tục, để tìm các thực thể bằng khóa chính của chúng và để truy vấn trên các thực thể.
Tập hợp các thực thể có thể được quản lý bởi một cá thể EntityManager nhất định được xác định bởi một đơn vị bền vững. Một đơn vị bền vững xác định tập hợp tất cả các lớp có liên quan hoặc được nhóm bởi ứng dụng và các lớp này phải được định vị trong ánh xạ của chúng với một cơ sở dữ liệu duy nhất.

Trình chạy dòng lệnh của trình quản lý thực thể người dùng

Các CommandLineRunner giao diện được sử dụng để chỉ ra rằng đậu này phải được chạy ngay sau khi ứng dụng bối cảnh mùa xuân được khởi tạo.

Chúng tôi đang thực hiện một vài phương pháp đơn giản trên UserService.

@Component
public class UserEntityManagerCommandLineRunner implements CommandLineRunner {

 private static final Logger log = LoggerFactory.getLogger(UserEntityManagerCommandLineRunner.class);

 @Autowired
 private UserService userService;

 @Override
 public void run(String...args) {

  log.info("-------------------------------");
  log.info("Adding Tom as Admin");
  log.info("-------------------------------");
  User tom = new User("Tom", "Admin");
  userService.insert(tom);
  log.info("Inserted Tom" + tom);

  log.info("-------------------------------");
  log.info("Finding user with id 1");
  log.info("-------------------------------");
  User user = userService.find(1 L);
  log.info(user.toString());

  log.info("-------------------------------");
  log.info("Finding all users");
  log.info("-------------------------------");
  log.info(userService.findAll().toString());
 }
}

Những điều quan trọng cần lưu ý:

  • @Autowired private UserService userService: Tự động kích hoạt dịch vụ người dùng.
  • Phần còn lại của công cụ là đơn giản!

Dữ liệu mùa xuân JPA

Spring Data nhằm cung cấp một mô hình nhất quán để truy cập dữ liệu từ các loại kho dữ liệu khác nhau.

UserService (mà chúng tôi đã tạo trước đó) chứa rất nhiều mã dư thừa có thể dễ dàng tổng quát hóa. Spring Data nhằm mục đích đơn giản hóa đoạn mã dưới đây.

@Repository
@Transactional
public class UserService {

 @PersistenceContext
 private EntityManager entityManager;

 public long insert(User user) {
  entityManager.persist(user);
  return user.getId();
 }

 public User find(long id) {
  return entityManager.find(User.class, id);
 }

 public List < User > findAll() {
  Query query = entityManager.createNamedQuery(
   "query_find_all_users", User.class);
  return query.getResultList();
 }
}

Theo như JPA có liên quan, có hai mô-đun Dữ liệu mùa xuân mà bạn cần biết:

  1. Spring Data Commons : Định nghĩa các khái niệm chung cho tất cả các mô-đun Dữ liệu mùa xuân.
  2. Spring Data JPA : Cung cấp khả năng tích hợp dễ dàng với các kho lưu trữ JPA.

CrudRepository

CrudRepository là lớp kho lưu trữ lõi được xác định trước (trong Spring Data Commons) cho phép các chức năng CRUD cơ bản trên kho lưu trữ. Các phương pháp quan trọng được hiển thị bên dưới.

public interface CrudRepository < T, ID extends Serializable >
 extends Repository < T, ID > {

  < S extends T > S save(S entity);

  T findOne(ID primaryKey);

  Iterable < T > findAll();

  Long count();

  void delete(T entity);

  boolean exists(ID primaryKey);

  // … more functionality omitted.
 }

JpaRepository

JpaRepository (được định nghĩa trong Spring Data JPA) là giao diện kho lưu trữ dành riêng cho JPA.

public interface JpaRepository < T, ID extends Serializable >
 extends PagingAndSortingRepository < T, ID > , QueryByExampleExecutor < T > {

Bây giờ chúng tôi sẽ sử dụng JpaRepository để quản lý thực thể Người dùng. Đoạn mã dưới đây cho thấy các chi tiết quan trọng. Chúng tôi muốn UserRepository quản lý thực thể Người dùng, thực thể này có khóa chính là loại Long.

package com.example.h2.user;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository < User, Long > {}

User Repository CommandLineRunner

Đoạn mã dưới đây rất đơn giản. CommandLineRunner giao diện được sử dụng để chỉ ra rằng bean này phải được chạy ngay sau khi ngữ cảnh ứng dụng Spring được khởi tạo. Chúng tôi đang thực hiện một số phương pháp đơn giản trên UserRepository.

package com.example.h2;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import com.example.h2.user.User;
import com.example.h2.user.UserRepository;

@Component
public class UserRepositoryCommandLineRunner implements CommandLineRunner {

 private static final Logger log = LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class);

 @Autowired
 private UserRepository userRepository;

 @Override
 public void run(String...args) {
  User harry = new User("Harry", "Admin");
  userRepository.save(harry);
  log.info("-------------------------------");
  log.info("Finding all users");
  log.info("-------------------------------");
  for (User user: userRepository.findAll()) {
   log.info(user.toString());
  }
 }

}

Những điều quan trọng cần lưu ý:

  • @Autowired private UserRepository userRepository: Tự động nối dây kho lưu trữ người dùng.
  • Phần còn lại của công cụ là đơn giản.

Bảng điều khiển H2

Chúng tôi sẽ bật bảng điều khiển h2 trong /src/main/resources/application.properties :

spring.h2.console.enabled=true

Bạn có thể khởi động ứng dụng bằng cách chạy H2InMemoryDbDemoApplication dưới dạng ứng dụng Java.

Bạn cũng có thể chạy H2-Console trên trình duyệt:

  • http: // localhost: 8080 / h2-console
  • Sử dụng   db url jdbc:h2:mem:testdb.

Ví dụ về mã hoàn chỉnh

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project
	xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>jpa-in-10-steps</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>jpa-with-in-memory-db-in-10-steps</name>
	<description>Demo project for in memory database H2</description>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.BUILD-SNAPSHOT</version>
		<relativePath/>
		<!-- lookup parent from repository -->
	</parent>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
		<pluginRepository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>
</project>

/src/main/java/com/example/h2/h2inmemorydbdemoapplication.java:

package com.example.h2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class H2InMemoryDbDemoApplication {

 public static void main(String[] args) {
  SpringApplication.run(H2InMemoryDbDemoApplication.class, args);
 }
}

/src/main/java/com/example/h2/user/user.java:

package com.example.h2.user;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

@Entity
@NamedQuery(query = "select u from User u", name = "query_find_all_users")
public class User {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;

 private String name; // Not perfect!! Should be a proper object!
 private String role; // Not perfect!! An enum should be a better choice!

 protected User() {}

 public User(String name, String role) {
  super();
  this.name = name;
  this.role = role;
 }

 public Long getId() {
  return id;
 }

 public String getName() {
  return name;
 }

 public String getRole() {
  return role;
 }

 @Override
 public String toString() {
  return String.format("User [id=%s, name=%s, role=%s]", id, name, role);
 }

}

/src/main/java/com/example/h2/user/userrepository.java:

package com.example.h2.user;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository < User, Long > {}

/src/main/java/com/example/h2/user/userservice.java:

package com.example.h2.user;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;

import org.springframework.stereotype.Repository;

@Repository
@Transactional
public class UserService {

 @PersistenceContext
 private EntityManager entityManager;

 public long insert(User user) {
  entityManager.persist(user);
  return user.getId();
 }

 public User find(long id) {
  return entityManager.find(User.class, id);
 }

 public List < User > findAll() {
  Query query = entityManager.createNamedQuery(
   "query_find_all_users", User.class);
  return query.getResultList();
 }
}

/src/main/java/com/example/h2/userentitymanagercommandlinerunner.java:

package com.example.h2;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import com.example.h2.user.User;
import com.example.h2.user.UserService;

@Component
public class UserEntityManagerCommandLineRunner implements CommandLineRunner {

 private static final Logger log = LoggerFactory.getLogger(UserEntityManagerCommandLineRunner.class);

 @Autowired
 private UserService userService;

 @Override
 public void run(String...args) {

  log.info("-------------------------------");
  log.info("Adding Tom as Admin");
  log.info("-------------------------------");
  User tom = new User("Tom", "Admin");
  userService.insert(tom);
  log.info("Inserted Tom" + tom);

  log.info("-------------------------------");
  log.info("Finding user with id 1");
  log.info("-------------------------------");
  User user = userService.find(1 L);
  log.info(user.toString());

  log.info("-------------------------------");
  log.info("Finding all users");
  log.info("-------------------------------");
  log.info(userService.findAll().toString());
 }
}

/src/main/java/com/example/h2/userrepositorycommandlinerunner.java:

package com.example.h2;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import com.example.h2.user.User;
import com.example.h2.user.UserRepository;

@Component
public class UserRepositoryCommandLineRunner implements CommandLineRunner {

 private static final Logger log = LoggerFactory.getLogger(UserRepositoryCommandLineRunner.class);

 @Autowired
 private UserRepository userRepository;

 @Override
 public void run(String...args) {
  User harry = new User("Harry", "Admin");
  userRepository.save(harry);
  log.info("-------------------------------");
  log.info("Finding all users");
  log.info("-------------------------------");
  for (User user: userRepository.findAll()) {
   log.info(user.toString());
  }
 }

}

/src/main/resources/application.properties:

spring.h2.console.enabled=true
#logging.level.org.hibernate=debug
spring.jpa.show-sql=true

/src/main/resources/data.sql:

insert into user (id, name, role) values (101, 'Ranga', 'Admin');
insert into user (id, name, role) values (102, 'Ravi', 'User');
insert into user (id, name, role) values (103, 'Satish', 'Admin');

/src/test/java/com/example/h2/h2inmemorydbdemoapplicationtests.java:

package com.example.h2;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class H2InMemoryDbDemoApplicationTests {

 @Test
 public void contextLoads() {}

}

/src/test/java/com/example/h2/user/userrepositorytest.java:

package com.example.h2.user;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.h2.user.UserRepository;

@DataJpaTest
@RunWith(SpringRunner.class)
public class UserRepositoryTest {

 @Autowired
 UserRepository userRepository;

 @Autowired
 TestEntityManager entityManager;

 @Test
 public void check_todo_count() {
  assertEquals(3, userRepository.count());
 }
}

Và đó là nó!

17 hữu ích 0 bình luận 227k xem chia sẻ

Có thể bạn quan tâm

loading