1 概述 Spring Data JPA是一个框架,对 Java Persistence API 进行再次封装。 Java Persistence API 是Java EE中的规范,它的实现产品有最有名的如Hibernate、Eclipse Link等,都是ORM框架。而ORM的本质是对JDBC进行了封装。ORM可以让程序员以OO的思维来操作关系数据库。
使用Spring Data JPA有以下优点:
不再需要手动建表 不再需要 写插入SQL 不再需要 写修改SQL 不再需要 写删除SQL 简单的 查询SQL, 不要写(查找一条和查找所有记录),复杂的需要写 2 关键技术 如何使用Spring Data JPA呢?
第一步,在Spring boot工程中引入依赖。
org.springframework.boot spring-boot-starter-data-jpa 第二步,建立一个数据库,并建立数据库访问的用户名和密码,并设置数据库访问权限。过程略。第三步,设置数据库连接属性。在application.properties配置文件中添加以下配置:
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/liyongzhendb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC spring.datasource.username=liyongzhendb spring.datasource.password=liyongzhendb
#Spring Boot 2.0 includes HikariDataSource by default spring.datasource.type = com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.connection-timeout=20000 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.maximum-pool-size=12 spring.datasource.hikari.idle-timeout=300000 spring.datasource.hikari.max-lifetime=1200000 spring.datasource.hikari.auto-commit=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect spring.jpa.properties.hibernate.id.new_generator_mappings=false spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.hibernate.ddl-auto=update spring.jpa.open-in-view=false spring.jpa.show-sql=true 配置很通俗易懂。仅介绍下spring.jpa.hibernate.ddl-auto,它的值是update,意思是实体有变动,会相应的修改表,如实体添加属性,表会相应添加字段。
更多application.properties配置见官方文档 https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
第五步,创建实体。实体即是轻量级持久对象域。通常,实体表示关系数据库中的表,并且每个实体实例对应于该表中的一行。更多理论参数Java EE官方文档 https://docs.oracle.com/javaee/6/tutorial/doc/bnbqa.html
创建的实体如下:
package com.wangshenghua.model;
import java.time.LocalDate; import java.util.Arrays;
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.validation.constraints.Email; import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size;
import org.springframework.format.annotation.DateTimeFormat;
@Entity @Table(name = “user”) public class User {
@NotNull @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @NotNull(message = "请输入姓名") @Size(min = 2, max = 30, message = "姓名在2~30个字符之间") private String name; @NotNull(message = "请输入年龄") @Min(message = "年龄至少15岁", value = 15) private Integer age; @NotNull(message = "请选择性别") private String gender; @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate dateOfBirth; @NotEmpty(message = "请输入邮箱") @Email(message = "邮箱格式不对") private String email; @NotNull @Size(min = 1, max = 5, message = "选择课程") private String[] course; @NotEmpty(message = "请选择故乡") private String hometown; @NotEmpty(message = "请选择兴趣爱好") private String[] hobbies; private String memo; public User() { } public User(@NotNull long id, @NotNull(message = "请输入姓名") @Size(min = 2, max = 30, message = "姓名在2~30个字符之间") String name, @NotNull(message = "请输入年龄") @Min(message = "年龄至少15岁", value = 15) Integer age, @NotNull(message = "请选择性别") String gender, @NotEmpty(message = "请输入邮箱") @Email(message = "邮箱格式不对") String email) { this.id = id; this.name = name; this.age = age; this.gender = gender; this.email = email; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public LocalDate getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(LocalDate dateOfBirth) { this.dateOfBirth = dateOfBirth; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String[] getCourse() { return course; } public void setCourse(String[] course) { this.course = course; } public String getHometown() { return hometown; } public void setHometown(String hometown) { this.hometown = hometown; } public String[] getHobbies() { return hobbies; } public void setHobbies(String[] hobbies) { this.hobbies = hobbies; } public String getMemo() { return memo; } public void setMemo(String memo) { this.memo = memo; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + ", dateOfBirth=" + dateOfBirth + ", email=" + email + ", course=" + Arrays.toString(course) + ", hometown=" + hometown + ", hobbies=" + Arrays.toString(hobbies) + ", memo=" + memo + "]"; }} 实体有3个关键点:其一注释@Entity指定此类是一个实体。其二注释@Table指定此实体在关系数据库中映射的表。其三注释@Id和@GeneratedValue用于指定主键和主键生成策略。
第六步,创建实现CrudRepository接口的接口。实现CrudRepository接口就具备了CURD的能力。
package com.wangshenghua.dao;
import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository;
import com.wangshenghua.model.User;
@Repository public interface UserRepository extends CrudRepository<User, Long> {
} Spring框架在分层上规划特别好。数据库访问层,使用注释@Repository。别使用@Controller或@Service哦。
CrudRepository接口是泛型,这里需要代入具体的实体名User,及实体的主键类型Long。
第七步,控制器层如何调用UserRepository?
package com.wangshenghua.controller;
import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional;
import javax.validation.Valid;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping;
import com.wangshenghua.dao.UserRepository; import com.wangshenghua.model.User;
@Controller public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class); final static Map<String, String> RADIO_GENDER = Collections.unmodifiableMap(new LinkedHashMap<String, String>() { private static final long serialVersionUID = 1L; { put("男", "男"); put("女", "女"); } }); final static Map<String, String> CHECK_COURSE = Collections.unmodifiableMap(new LinkedHashMap<String, String>() { private static final long serialVersionUID = 1L; { put("Java程序设计", "java"); put("Spring", "spring"); put("MySQL", "mysql"); put("HTML", "html"); } }); final static Map<String, String> SELECT_HOMETOWN = Collections.unmodifiableMap(new LinkedHashMap<String, String>() { private static final long serialVersionUID = 1L; { put("郴州", "郴州"); put("耒阳", "耒阳"); put("广州", "广州"); } }); final static Map<String, String> SELECT_HOBBIES = Collections.unmodifiableMap(new LinkedHashMap<String, String>() { private static final long serialVersionUID = 1L; { put("打球", "打球"); put("听歌", "听歌"); put("玩游戏", "玩游戏"); put("写代码", "写代码"); put("吃烧烤", "吃烧烤"); } }); @Autowired private UserRepository userDao; /** 控制器的方法 **/ @GetMapping("/") public String index() { return "redirect:/allUser"; } @GetMapping("/allUser") public String allUser(Model model) { model.addAttribute("users", userDao.findAll()); return "list-user"; } @GetMapping("/adduser") public String showAddUserForm(Model model) { User user = new User(); model.addAttribute("user", user); model.addAttribute("radioItems", RADIO_GENDER); model.addAttribute("checkItems", CHECK_COURSE); model.addAttribute("selectItems", SELECT_HOMETOWN); model.addAttribute("multipleItems", SELECT_HOBBIES); return "add-user"; } @PostMapping("/adduser") public String addUser(@Valid User user, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { model.addAttribute("user", user); model.addAttribute("radioItems", RADIO_GENDER); model.addAttribute("checkItems", CHECK_COURSE); model.addAttribute("selectItems", SELECT_HOMETOWN); model.addAttribute("multipleItems", SELECT_HOBBIES); return "add-user"; } userDao.save(user); return "redirect:/allUser"; } @GetMapping("/edit/{id}") // {id}是占位符 public String showUpdateForm(@PathVariable("id") long id, Model model) { // @PathVariable 路径变量 Optional<User> user = userDao.findById(id); model.addAttribute("user", user); return "update-user"; } @PostMapping("/update/{id}") public String updateUser(@PathVariable("id") long id, @Valid User user, BindingResult bindingResult) { if (bindingResult.hasErrors()) { return "update-user"; } userDao.save(user); return "redirect:/allUser"; } @GetMapping("/delete/{id}") public String deleteUser(@PathVariable("id") long id, Model model) { userDao.deleteById(id); return "redirect:/allUser"; }} 别看代码多,关键代码仅几行:
@Autowired private UserRepository userDao;采用自动注入的方式注入 UserRepository 实例。
userDao.findAll()查找所有,实则调用CrudRepository接口的方法Iterable findAll();
userDao.findById(id)根据主键查找实体, 实则调用CrudRepository接口的方法Optional findById(ID id)
userDao.deleteById(id)根据主键删除实体, 实则调用CrudRepository接口的方法void deleteById(ID id);
第八步,在Spring boot工程的主程序@SpringBootApplication添加扫描实体所在包和Repository所在包的配置。如下:
@EnableTransactionManagement @EnableJpaRepositories(basePackages = “com.wangshenghua.dao”) @EntityScan(basePackages = “com.wangshenghua.model”, basePackageClasses = { Application.class, Jsr310JpaConverters.class }) @EnableTransactionManagement表示启用事务管理
@EnableJpaRepositories表示启用 Repository 操作数据库
@EntityScan表示扫描实体
3 总结 本节课程我们学习Spring Data JPA操作关系数据库,来实现增删改查功能。使用 Spring Data JPA操作关系数据库非常简单(相对JDBC而言)。关键步骤仅8步。
越是使用简单的东东,底层代码越是复杂。不仅是Spring对底层进行抽象封装,Java EE组织对JDBC的抽象和封装从未止步。正是有这此组织和个人的除出,才有Java今天的地位。
本节课程源码已经上传到github,可以前往下载。