使用SpringDataJPA操作数据库

1、SpringDataJPA

JPA(Java Persistence API) 是指Java中数据对象持久化的过程。所谓持久化是指将内存中创建的数据对象永久的保存在数据库中,即在Java中我们通过JDBC连接数据库并进行增删改查的过程。在JDBC的基础上又出现了许多持久层框架用于简化Java和数据库的交互,其中两个较为流行的框架–Hibernate和Mybatis。但是我们依旧需要在Java中编写调用数据库的接口函数,为了统一和简化这个过程,SpringDataJPA在Hibernate的基础上基于JPA标准统一定义了操作数据库函数的相关接口,这样我们可以按照规范直接使用定义好的接口函数来操作数据库。

首先通过gradle引入SpringDataJPA的依赖以及JDBC数据库连接依赖

dependencies {
	implementation('org.springframework.boot:spring-boot-starter-data-jpa')	//Spring Data JPA
	implementation('mysql:mysql-connector-java')
	......
}

接着在application.properties文件中配置数据库连接和设置SpringJPA。其中ddl-auto代表对数据表的操作方式,如果为create则每次都会为实体对象创建新的数据表,例如为User对象在数据库中创建user表,如果已经存在则会删除原有表。若为update代表在原有表的基础上进行更新。
spring jpa默认使用下划线的方式转换Java属性和数据库字段,例如userName会被映射到数据表的user_name字段,若想关闭可以设置hibernate.naming.physical-strategy为PhysicalNamingStrategyStandardImpl

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost/blog
spring.datasource.username=root
spring.datasource.password=123456

# 控制台打印数据库查询语句
spring.jpa.show-sql=true
# 在原有数据表上进行更新
spring.jpa.hibernate.ddl-auto=update
# 命名映射策略
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

2、实体类

如下所示创建一个Java实体类User,在其中定义五个字段并生成对应的getter/setter方法,并且需要创建一个空的构造函数User()
使用@Entity表明这是一个实体类,并使用@Id指明主键,@GeneratedValue指明主键的生成策略,这里使用IDENTITY代表由数据库自动生成自增主键。通过@Column注解指定属性在数据库中对应的列名,并设置是否唯一、是否不为空。

通过注解@Size对属性进行校验,@NotEmpty对为空进行校验并返回提示信息,@Email对邮箱格式进行校验。

通过@OneToOne可以实现数据表之间的一对一映射,类似地还有@OneToMany、@ManyToMany
其中CascadeType属性指定级联操作,例如

  • PERSIST:在保存User类时相关的UserImage也会保存到数据表中
  • REMOVE:删除当前实体时,与它有映射关系的实体也会跟着被删除
  • DETACH:删除当前实体和其外键,但不会删除映射的实体
  • REFRESH:操作数据表时先进行刷新操作
  • MERGE:当User中的数据改变,会相应地更新UserImage中的数据
  • ALL:拥有以上所有级联操作权限
    通过@JoinColumn指定连接表的外键所在列
package com.tory.blog.entity;

import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Date;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import javax.validation.constraints.Email;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

	@Column(name = "username", unique = true, nullable = false)
    private String username;
    
    private String password;

	@NotEmpty(message = "邮箱不能为空")
	@Size(max=50)
	@Email(message= "邮箱格式不对" ) 
    private String email;

	@OneToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinColumn(name = "img_id")
    private UserImage userImg;

	@CreationTimestamp  // 由数据库自动创建时间
    private Date createTime;

    public User() {
    }

    //... getter/seter方法...
}

3、定义JPA接口

SpringDataJPA把对数据库的相关操作都封装在JPA接口中,通过继承接口就可以直接使用操作数据库的方法。按照不同用途SpringJPA提供了不同的接口,比如CrudRepository封装了常用的数据库增删改查操作的接口,在此基础上PagingAndSortingRepository接口封装了数据分页的函数。进一步JpaRepository实现了实体查找、排序等相关方法。

如下所示为CrudRepository接口定义的方法

public interface CrudRepository<T, ID> extends Repository<T, ID> {
	//保存实体对象
	<S extends T> S save(S entity);
	//保存多个实体对象
	<S extends T> Iterable<S> saveAll(Iterable<S> entities);
	
	//根据id查找对象
	Optional<T> findById(ID id);
	//根据id判断对象是否存在
	boolean existsById(ID id);
	
	//返回所有实体
	Iterable<T> findAll();
	//根据id查询多个实体
	Iterable<T> findAllById(Iterable<ID> ids);
	//统计实体个数
	long count();

	//根据id进行删除
	void deleteById(ID id);
	//根据实体对象进行删除
	void delete(T entity);
	//删除多个实体
	void deleteAll(Iterable<? extends T> entities);
	//删除所有
	void deleteAll();
}

我们可以在上面接口的基础上自定义操作数据库的接口函数,如下所示为继承自CrudRepository的UserRepo接口,并且新增自定义了一些数据库操作函数。

在继承CrudRepository时通过泛型<User, Long>指定返回主键为Long类型的User对象,接口中的方法会默认将返回的数据映射为User类型,并且将主键映射为Long。如果查询结果返回其他对象类型会出现映射错误。

SpringJPA可以根据规范的方法名自动生成相应的数据库操作方法,例如我们定义了findByUsername()方法,它会自动根据username字段查找并返回User对象数组。Distinct可以返回不重复的结果,OrderBy可以对返回结果进行排序。

可以使用@Query注解来自定义查询函数,其中占位符?1代表第一个参数。也可以使用占位符:firstname,并在之后用@Param指定

package com.tory.blog.repository;

import com.tory.blog.entity.User;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface UserRepo extends CrudRepository<User, Long> {
    //根据方法名生成操作函数
    List<User> findByUsername(String username);

    //使用Distinct
    List<User> findDistinctByUsername(String username);

    //使用OrderBy
    List<User> findByUsernameOrderByCreateTime(String username);

	//自定义查询
	@Query("select u from User u where u.emailAddress = ?1")
  	User findByEmailAddress(String emailAddress);

	//使用@Param
	@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  	User findByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname);
}

如下所示UserRepository继承自JpaRepository,并定义findByNameLike()根据用户名进行模糊查询并返回分页的查询结果Page,该方法除了名字外还需要传入分页对象Pageable。在UserServiceImpl类中构建分页对象pageable并调用该方法实现分页查询。

public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findByNameLike(String name, Pageable pageable);
}

public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    public List<User> listUsersByNameLike(String name, int pageIndex, int pageSize) {
        name = "%" + name + "%";    // 模糊查询
        Sort sort = Sort.by(Sort.Order.desc("id")); //根据用户id进行排序
        Pageable pageable = PageRequest.of(pageIndex, pageSize, sort);  //分页对象
        Page<User> page = userRepository.findByNameLike(name, pageable);
        List<User> userList = page.getContent();    //获取数据
        return userList;
    }

4、Spring中使用接口

如下所示直接在Spring的controller层中调用JPA接口完成和数据库的交互,首先通过@Autowire实例化userRepo对象完成数据库相关操作。

其中用userRepo.findAll()查询所有用户的信息。
userRepo.findById()根据id查询用户信息,需要注意的是该方法返回的是Optional类型的结果,如果查询到结果会放在Optional中,若无则Optional为空。可以通过isPresent()判断结果集是否为空,若非空可以通过get()获取结果集中的数据。
通过userRepo.save()实现对象数据的保存。userRepo.deleteById()实现对象的删除。

package com.tory.blog.controller;

import com.tory.blog.entity.User;
import com.tory.blog.repository.UserRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.Date;

@RestController
@RequestMapping("user")
public class UserController {
    @Autowired
    private UserRepo userRepo;

    //查询所有用户信息
    @GetMapping("list")
    public ModelAndView index() {
        return new ModelAndView("user/list", "userList", userRepo.findAll());
    }

    //根据id查询单个用户信息
    @GetMapping("{id}")
    public ModelAndView getById(@PathVariable("id") Long id) {
        ModelAndView modelAndView = new ModelAndView();
        if (userRepo.findById(id).isPresent()) {						//若查询结果不为空
            modelAndView.addObject("user", userRepo.findById(id).get());
        }
        modelAndView.setViewName("user/user");
        return modelAndView;
    }

    //新增用户
    @GetMapping("add")
    public ModelAndView add() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("user", new User());
        modelAndView.addObject("title", "添加用户");
        modelAndView.setViewName("user/form");
        return modelAndView;
    }

    //修改用户信息
    @GetMapping("modify/{id}")
    public ModelAndView modify(@PathVariable("id") Long id) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("user", userRepo.findById(id).get());
        modelAndView.addObject("title", "修改信息");
        modelAndView.setViewName("user/form");
        return modelAndView;
    }

    //保存用户user对象
    @PostMapping("save")
    public ModelAndView saveUser(User user) {
        user.setCreateTime(new Date());
        userRepo.save(user);
        return new ModelAndView("redirect:/user/list");
    }

    //删除用户
    @GetMapping("delete/{id}")
    public ModelAndView delete(@PathVariable("id") Long id) {
        userRepo.deleteById(id);
        return new ModelAndView("redirect:/user/list");
    }
}

前端表单页面如下

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:text="${title}"></title>
    <script th:replace="common/head::static"></script>
</head>
<body>
<form action="/user/save" method="post" th:object="${user}">
    <input type="hidden" name="id" th:value="*{id}"/>
    姓名:<input type="text" name="username" th:value="*{username}"/><br>
    邮箱:<input type="text" name="email" th:value="*{email}"/> <br>
    密码:<input type="password" name="password" th:value="*{password}"/> <br>
    <input type="submit" value="提交">
</form>
</body>
</html>

在这里插入图片描述
当用户访问/user/add页面,填写并提交表单后会发送post请求到/user/save,来到Controller的saveUser()方法,这时Spring会根据表单中的name属性生成相应的User对象,之后调用userRepo.save()将user对象持久化到数据库中。数据表user如下所示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值