SpringBoot+SpringData JPA操作Mysql数据库

一、什么是JPA

        JPA本身并不是一种框架,是一种规范,其全称是Java Persistence API,是是Sun官方提出的Java持久化规范,而他的出现主要是为了简化现有的持久化开发工作和整合ORM技术,并且其是在充分吸收了现有Hibernate,TopLink,JDO等ORM框架的基础上发展而来的,具有易于使用,伸缩性强等优点。

        Spring Data JPA的官网对介绍:Spring Data JPA是Spring Data系列的一部分,可以轻松实现基于JPA的存储库。该模块处理对基于JPA的数据访问层的增强的支持。这使得使用数据访问技术构建Spring供电的应用程序变得更加容易。

        Spring Data JPA旨在通过减少实际需要的数量来显着提高数据访问层的实现。作为开发人员,您编写存储库接口(包括自定义查找器方法),Spring将自动提供实现。

SpringBoot+SpringData JPA操作Mysql数据库_第1张图片

主要特点:

  • 基于Spring和JPA构建存储库的复杂支持
  • 支持Querydsl谓词,从而支持类型安全的JPA查询
  • 域类的透明审计
  • 分页支持,动态查询执行,整合自定义数据访问代码的能力
  • @Query引导时间验证注释查询
  • 支持基于XML的实体映射

二、Spring Data JPA 方法介绍

  •  常用规则速查
关键字 示例 说明 JPQL片段示例

And

findByLastnameAndFirstname

并且

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

等于

… where x.firstname = ?1

Between

findByStartDateBetween

两者之间

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

小于

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

小于等于

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

大于

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

大于等于

… where x.age >= ?1

After

findByStartDateAfter

之后(时间)

… where x.startDate > ?1

Before

findByStartDateBefore

之前(时间) 

… where x.startDate < ?1

IsNull

findByAgeIsNull

等于Null

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

不等于Null

… where x.age not null

Like

findByFirstnameLike

模糊查询。查询件中需要自己加 %

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

不在模糊范围内。查询件中需要自己加 %

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

以某开头

… where x.firstname like ?1(parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

以某结束

… where x.firstname like ?1(parameter bound with prepended %)

Containing

findByFirstnameContaining

包含某

… where x.firstname like ?1(parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

排序

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

不等于

… where x.lastname <> ?1

In

findByAgeIn(Collection ages)

某范围内

… where x.age in ?1

NotIn

findByAgeNotIn(Collection age)

某范围外

… where x.age not in ?1

True

findByActiveTrue()

真 

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

忽略大小写

… where UPPER(x.firstame) = UPPER(?1)

  • Spring Data 解析方法名--规则说明

 规则描述

        按照Spring data 定义的规则,查询方法以find|read|get开头(比如 find、findBy、read、readBy、get、getBy),涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。

        如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。

 举例说明

       比如 findByUserAddressZip()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 AccountInfo 类型):

  1. 先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
  2. 从右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 AccountInfo 的一个属性;
  3. 接着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 "AccountInfo.user.addressZip" 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 "AccountInfo.user.address.zip" 的值进行查询。

        可能会存在一种特殊情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上 "_" 以显式表达意图,比如 "findByUser_AddressZip()" 或者 "findByUserAddress_Zip()"。(强烈建议:无论是否存在混淆,都要在不同类层级之间加上"_" ,增加代码可读性)

当查询条件为null时。

   举例说明如下:

  • 实体定义:对于一个客户实体Cus,包含有name和sex,均是String类型。
  • 查询方法定义:List findByNameAndSex(String name,String sex);
  • 使用时:dao.findByNameAndSex(null, "男");
  • 后台生成sql片断:where (cus0_.name is null) and cus0_.sex=?
  • 结论:当查询时传值是null时,数据库中只有该字段是null的记录才符合条件,并不是说忽略这个条件。也就是说,这种查询方式,只适合于明确查询条件必须传的业务,对于动态查询(条件多少是动态的,例如一般的查询列表,由最终用户使用时决定输入那些查询条件),这种简单查询是不能满足要求的。

 排序

List findBySexOrderByName(String sex); //名称正序(正序时,推荐此方式,简单)
List findBySexOrderByNameAsc(String sex); //名称正序(效果同上)
List findBySexOrderByNameDesc(String sex); //名称倒序

  结果限制

/**
* 根据父ID,得到排序号最大的bo。
* 用于预计算新资源的排序号。
*/
Resource findFirstByFather_idOrderByOrderNumDesc(Long fatherId);
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page queryFirst10ByLastname(String lastname, Pageable pageable);
Slice findTop3ByLastname(String lastname, Pageable pageable);
List findFirst10ByLastname(String lastname, Sort sort);
List findTop10ByLastname(String lastname, Pageable pageable);  

Spring Data JPA使用findAllOrderBy坑

List findAllOrderByCreateTimeDesc();

可以看到,我希望在一个表中查询所有的数据,并按照createTime这个字段进行排序,这样的写法看似正确的,会报错

其实,正确的写法是:

List findAllByOrderByCreateTimeDesc();

4、计数

Long count();
Long countByLastname(String lastname);

5、删除

void deleteByProject_Id(Long id);
void deleteByProject_Cus_id(Long id);

三、复杂条件查询-多条件动态查询

刚开始使用springdata的时候,只会用findByName这样的简单查询,这样写dao层确实非常的快,但是在我们做筛选功能的时候,这样的查询似乎很难满足我们的需求,但是都已经用上的springdata又不想再去写mybatis这样在xml里面判断是否为Null。

我们首先会想到可以使用@Query注解,这种方式可以直接在Repository里面写sql,但是这种方式的问题就是太麻烦了,而且非常容易出错,扩展性也很差,还不如直接用mybatis。

3.1 Example

用example可以最快速的完成支持所有参数的筛选功能,像这样的代码:

/**
 * 查询用户列表
 *
 * @return 用户列表
 */
@GetMapping("listByKeyWord")
public String list(ModelMap model, OyUser user) {
    //创建匹配器,即如何使用查询条件
    ExampleMatcher matcher = ExampleMatcher.matching()
            //模糊查询匹配开头,即{username}% .withMatcher("nickName", ExampleMatcher.GenericPropertyMatchers.startsWith())
            .withMatcher("nickName", ExampleMatcher.GenericPropertyMatchers.contains())
            //全部模糊查询,即%{address}%
            .withMatcher("name" ,ExampleMatcher.GenericPropertyMatchers.contains())
            .withMatcher("account" ,ExampleMatcher.GenericPropertyMatchers.contains())
            //忽略字段,即不管password是什么值都不加入查询条件
            .withIgnorePaths("sex","age")
            // 忽略属性:id。因为是基本类型,需要忽略掉
            .withIgnorePaths("id");
    //创建实例
    Example example = Example.of(user ,matcher);
    //查询
    List userList = oyUserRepository.findAll(example);
    model.put("users",userList);
    return "user";
}

Example会将为null的字段自动过滤掉,不会作为筛选条件,ExampleMatch是为了支持一些稍微复杂一些的查询,比如如果有int类型的id就需要用withIgnorePaths()忽略掉,因为Int类型默认为0,而不是Null。
如果只是简单的字符串匹配的话,可以直接用:

 Example example = Example.of(user); 来构造Example。

限制

  1. 属性不支持嵌套或者分组约束,比如这样的查询 firstname = ?0 or (firstname = ?1 and lastname = ?2)
  2. 灵活匹配只支持字符串类型,其他类型只支持精确匹配。

3.2 Criteria

Criteria查询是Jpa中最强的使用方式了,所有的场景应该都能完成。
首先Repository要继承JpaSpecificationExecutor:

public interface OyUserRepository extends JpaRepository , JpaSpecificationExecutor {
}

然后就是构造动态查询:

/**
 * 查询用户列表2
 *
 * @return 用户列表
 */
@GetMapping("listByKeyWord2")
public String listByKeyWord2(ModelMap model, OyUser oyUser) {
    Specification querySpeci = new Specification() {
        @Override
        public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
            List predicates = new ArrayList();
            if (oyUser.getName() != null) {
                predicates.add(criteriaBuilder.like(root.get("name"), "%" + oyUser.getName() + "%"));
            }
            if (oyUser.getAccount() != null) {
                predicates.add(criteriaBuilder.like(root.get("account"), "%" + oyUser.getAccount() + "%"));
            }
            if (null != oyUser.getAge()) {
                predicates.add(criteriaBuilder.gt(root.get("age"), oyUser.getAge()));
            }
            if (null != oyUser.getSex()) {
                predicates.add(criteriaBuilder.equal(root.get("sex"), oyUser.getSex()));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        }
    };
    Pageable pageable = PageRequest.of(0, 3);
    Page userListPage = oyUserRepository.findAll(querySpeci, pageable);
    model.put("users", userListPage.getContent());
    return "user";
}

其他

@Resource
private EntityManager entityManager;

/**
 * 查询用户列表1
 *
 * @return 用户列表
 */
@GetMapping("listByKeyWord1")
public String listByKeyWord1(ModelMap model, OyUser user) {
    //创建CriteriaBuilder安全查询工厂
    //CriteriaBuilder是一个工厂对象,安全查询的开始.用于构建JPA安全查询.
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    //创建CriteriaQuery安全查询主语句
    //CriteriaQuery对象必须在实体类型或嵌入式类型上的Criteria 查询上起作用。
    CriteriaQuery query = criteriaBuilder.createQuery(OyUser.class);
    //Root 定义查询的From子句中能出现的类型
    Root userRoot = query.from(OyUser.class);
    //Predicate 过滤条件 构建where字句可能的各种条件
    //这里用List存放多种查询条件,实现动态查询
    List predicatesList = new ArrayList<>();
    //name模糊查询 ,like语句
    if (user.getName() != null) {
        predicatesList.add(
                criteriaBuilder.and(
                        criteriaBuilder.like(
                                userRoot.get("name"), "%" + user.getName() + "%")));
    }
    //account模糊查询 ,like语句
    if (user.getAccount() != null) {
        predicatesList.add(
                criteriaBuilder.and(
                        criteriaBuilder.like(
                                userRoot.get("account"), "%" + user.getAccount() + "%")));
    }
    // itemPrice 小于等于 <= 语句
    if (user.getAge() != null) {
        predicatesList.add(
                criteriaBuilder.and(
                        criteriaBuilder.le(
                                userRoot.get("age"), user.getAge())));
    }
    //itemStock 大于等于 >= 语句 criteriaBuilder.ge
    //where()拼接查询条件
    query.where(predicatesList.toArray(new Predicate[predicatesList.size()]));

    TypedQuery typedQuery = entityManager.createQuery(query);
    //查询
    List userList = typedQuery.getResultList();
    model.put("users", userList);
    return "user";
}

四、SpringBoot 整合Spring Data JPA

  1. 在springboot中引入依赖
    
    
        mysql
        mysql-connector-java
        5.1.29
    
    
    
        org.springframework.boot
        spring-boot-starter-data-jpa
    
  2. 配置数据源
    spring:
        datasource:
            url: jdbc:mysql://127.0.0.1:3306/ouyangblog?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=utf8 
            username: ouyangcheng
            password: 123456
            driver-class-name: com.mysql.jdbc.Driver
            druid:
                initialSize: 1
                minIdle: 1
                maxActive: 50
                maxWait: 6000
                timeBetweenEvictionRunsMillis: 6000
                minEvictableIdleTimeMillis: 30000
                testWhileIdle: true
                testOnBorrow: true
                testOnReturn: true
                validationQuery: SELECT 1 from dual
                connectionProperties: config.decrypt=false # database connection plain password
  3. 持久层继承JpaRepository
    /**
     * @author oyc
     * @Title: OuYangUserRepositoryTest
     * @ProjectName ouyangblog
     * @Description: TODO
     * @date 2018/11/15 21:48
     */
    @Repository
    public interface OuYangUserRepository extends JpaRepository {
        OuYangUser findByAccount(String account);
    }
  4. 编写接口调用测试JPA的方法,从下图可以看到,我们可以直接注入持久层OuYangUserRepository ,使用JPA提供的方法来进行对数据库的操作

    SpringBoot+SpringData JPA操作Mysql数据库_第2张图片

package cn.com.ouyangblog.web.rest;

import cn.com.ouyangblog.domain.OuYangUser;
import cn.com.ouyangblog.repository.OuYangUserRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @author oyc
 * @Title: OuYangUserController
 * @ProjectName ouyangblog
 * @Description: TODO
 * @date 2018/11/15 21:41
 */
@Api(value = "用户模块")
@Controller
@RequestMapping("/user")
public class OuYangUserController {

    @Resource
    private OuYangUserRepository ouYangUserRepository;

    @ApiOperation(value = "获取用户列表")
    @RequestMapping(value = "/list", method = {RequestMethod.GET, RequestMethod.POST})
    public String list(HttpServletRequest request, HttpServletResponse response, Model model) {
        List userList = ouYangUserRepository.findAll();
        model.addAttribute("userList", userList);
        return "pages/userList";
    }

    @ApiOperation(value = "添加用户")
    @PostMapping("/adduser")
    public String addUser(OuYangUser bean,Model model) throws Exception {
        try {
            ouYangUserRepository.saveAndFlush(bean);
        } catch (Exception e) {
            e.printStackTrace();
        }
        List userList = ouYangUserRepository.findAll();
        model.addAttribute("userList", userList);
        return "pages/userList";
    }

    @ApiOperation(value = "删除用户")
    @PostMapping("/deluser")
    public ResponseEntity delUser(Integer id) throws Exception {
        try {
            ouYangUserRepository.deleteById(id);
            return ResponseEntity.ok(true);
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.ok(false);
        }
    }

}

SpringBoot+SpringData JPA操作Mysql数据库_第3张图片

 

 

参考:https://www.cnblogs.com/rulian/p/6434631.html

你可能感兴趣的:(SpringBoot)