zoukankan      html  css  js  c++  java
  • JPA访问数据库的几种方式

    JPA访问数据库的几种方式

     

    本文为原创,转载请注明出处:https://www.cnblogs.com/supiaopiao/p/10901793.html

    1. Repository

    1.1. 通过方法名称直接生成查询

    Keyword

    Sample

    JPQL snippet

    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

    … where x.age is null

    IsNotNull,NotNull

    findByAge(Is)NotNull

    … 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<Age> ages)

    … where x.age in ?1

    NotIn

    findByAgeNotIn(Collection<Age> ages)

    … 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)

    In和NotIn可以使用任何Collection的子类作为参数。

    案例:

    public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
        Page<User> findByName(String name);


        List<User> findByNameAndPhone(String name, String phone);

        User findUserById(Integer id);

        void deleteByIdIn(List<Integer> idList);
    }

    1.2. @Query注解

    可以使用Spring Data JPA 的@Query注解将查询绑定到repository的函数上。

    备注:注解到查询方法上的@Query执行顺序优先于下文中的@NamedQuery

    1.2.1. HQL

    注意:使用“:属性名”的时候,最好在接口上加上@Param注解,否则会报类似如下异常:

     

    public interface UserRepository extends JpaRepository<User, Integer>{
    @Query(value = "SELECT u FROM User u WHERE u.name like %?1%")//不加nativeQuery应使用HQL
    List<User> findTable2(String name);

    @Query(value = "SELECT u FROM User u WHERE u.name like %:name% and u.sex = :sex")
        List<User> findTable3(@Param("name") String name, @Param("sex")String sex);
    }

    1.2.2. 原生SQL

    1)@Query注解通过设置nativeQuery标识支持执行原生SQL查询语句

    public interface UserRepository extends JpaRepository<User, Integer>{
    @Query(value = "SELECT * FROM user WHERE name like %?1%", nativeQuery = true)
    List<User> findTable1(String name);

    }

    (2)Spring Data JPA通过原生SQL进行查询时,不能满足Page条件进行分页,所以可以通过countQuery标识执行count查询实现Page分页:

    public interface UserRepository extends JpaRepository<User, Integer>{
       @Query(value = "SELECT * FROM user WHERE name like %?1%",
                countQuery = "SELECT count(*) FROM user WHERE name like %?1%",
                nativeQuery = true)
        Page<User> findTable3(String name, Pageable pageable);
    }

    1.3. @Modifying注解

    1. @Query注解中编写JPQL实现DELETE和UPDATE操作的时候必须加上@Modifying注解,以通知Spring Data这是一个DELETE或UPDATE操作。
    2. UPDATE或者DELETE操作需要使用事务,此时需要定义Service层,在Service层的方法上添加事务操作@Transactional。
    3. Modifying查询语句中能用于void/int/Integer 返回类型,不能用于其他类型
    4. 注意JPQL不支持INSERT操作。

     

    代码示例:

    public interface UserRepository extends JpaRepository<User, Integer>{

    @Modifying
        @Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
        void updateTable(@Param("name") String name, @Param("id")Integer id);

    @Modifying
    @Query("delete from User u WHERE u.id in :idList")
    void deleteByIds(@Param("idList")List<Integer> idList);

    }

    2. 实体类中定义,在Repository中使用

    2.1. 命名查询@NamedQuery

    使用@NamedQuery为实体创建查询适用于定义少量查询。@NamedQuery需要声明在实体类上@NamedQuery可以实现命名HQL查询(或JPQL)

    备注:注解到查询方法上的@Query上文中执行顺序优先于@NamedQuery

    @Entity
    @NamedQuery(name = "Two.selectBySex",query = "select t from Two t where t.sex = ?1 group by id")
    public class Two {
        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="id",nullable=false)
        private Integer id;

        @Column(name="name",nullable=false)
        private String name;

        @Column(name="sex",nullable=false)
        private String sex;

    Set、get方法省略
    }

    Repository层代码展示

    public interface TwoRepository extends JpaRepository<Two, Integer>{
        //实体类上通过@NamedQuery注解实现查询
        List<Two> selectBySex(String sex);
    }

    Spring Data处理这些方法对命名查询的调用,以实体类名称开始,后接方法名,以点作连接符。所以NameQuery定义在实体上,而不是定义在方法上。

    2.2. 个实体类中有多个命名查询@NamedQueries

    上面我们演示了命名查询@NamedQuery的写法,当然JPA也支持多个@NamedQuery,那就是@NamedQueries

    @Entity
    @NamedQueries(value = {
            @NamedQuery(
                    name = "Two.selectBySex",
                    query = "select t from Two t where t.sex = ?1 group by id"),
            @NamedQuery(
                    name = "Two.findUserByPrimaryKey",
                    query = "select t from Two t where t.id = :id")
    })

    public class Two {
        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="id",nullable=false)
        private Integer id;

        @Column(name="name",nullable=false)
        private String name;

        @Column(name="sex",nullable=false)
        private String sex;

    Set、get方法省略
    }

    Repository层代码展示

    public interface TwoRepository extends JpaRepository<Two, Integer>{
        //实体类上通过@NamedQuery注解实现查询
        List<Two> selectBySex(String sex);
        Two findUserByPrimaryKey(@Param("id") Integer id);
    }

    Spring Data处理这些方法对命名查询的调用,以实体类名称开始,后接方法名,以点作连接符。所以NameQuery定义在实体上,而不是定义在方法上。

    2.3. 命名原生sql查询@NamedNativeQuery

    @NamedNativeQuery可以实现命名原生sql查询

    @Entity
    @NamedNativeQuery(name = "Three.selectBySex", query = "select * from three where sex = ?1 group by id", resultClass = Three.class)
    @Table(name="three")
    public class Three {
        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="id",nullable=false)
        private Integer id;

        @Column(name="name",nullable=false)
        private String name;

        @Column(name="sex",nullable=false)
        private String sex;

    Set、get方法省略
    }

    Repository层代码展示

    public interface ThreeRepository extends JpaRepository<Three, Integer>{
        //实体类上通过@NamedNativeQuery注解实现查询
        List<Three> selectBySex(String sex);
    }

    2.4. 个实体类中有多个命名查询@NamedNativeQueries

    上面我们演示了命名查询@NamedNativeQuery的写法,当然JPA也支持多个@NamedNativeQuery,那就是@NamedNativeQueries

    @Entity
    @NamedNativeQueries(value = {
            @NamedNativeQuery(
                    name = "Three.selectBySex",
                    query = "select * from three where sex = ?1 group by id",
                    resultClass = Three.class),
            @NamedNativeQuery(
                    name = "Three.findUserByPrimaryKey",
                    query = "select * from three where id = :id",
                    resultClass = Three.class)//resutlClass用来指定实体类,resutlSetMapping用来指定映射的名称
    })
    @Table(name="three")
    public class Three {
        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="id",nullable=false)
        private Integer id;

        @Column(name="name",nullable=false)
        private String name;

        @Column(name="sex",nullable=false)
        private String sex;

    Set、get方法省略
    }

    Repository层代码展示

    public interface ThreeRepository extends JpaRepository<Three, Integer>{
        //实体类上通过@NamedNativeQuery注解实现查询
        List<Three> selectBySex(String sex);
        Three findUserByPrimaryKey(@Param("id") Integer id);
    }

    3. 外部ORM文件中定义,在Repository中使用

    3.1. 命名查询named-query

    使用XML配置,向位于resources/META-INF文件夹下的orm.xml配置文件添加必要的<name-query/>元素。

    resources/META-INF下orm.xml配置:

    <?xml version="1.0" encoding="UTF-8" ?>
    <entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
                                         http://xmlns.jcp.org/xml/ns/persistence/orm_2_0.xsd" version="2.1">

        <!-- JPA Named Queries -->
        <named-query name="One.selectByName">
            <query>SELECT o FROM One o WHERE o.sex = ?1 group by id</query>
        </named-query>

        <named-query name="One.findUserByPrimaryKey">
            <query>SELECT o FROM One o WHERE o.id = :id</query>
        </named-query>
    </entity-mappings>

    Repository层代码展示

    public interface OneRepository extends JpaRepository<One, Integer>{
        //1.1 XML定义 命名查询
        List<One> selectByName(String name);
        One findUserByPrimaryKey(@Param("id") Integer id);
    }

    3.2. 原生SQL查询named-native-query

    使用XML配置,向位于resources/META-INF文件夹下的orm.xml配置文件添加必要的<named-native-query/>元素。

    resources/META-INF下orm.xml配置:

    <?xml version="1.0" encoding="UTF-8" ?>
    <entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
                                         http://xmlns.jcp.org/xml/ns/persistence/orm_2_0.xsd" version="2.1">

        <!-- JPA Named Native Queries -->
        <named-native-query name="One.selectByName1">
            <query>SELECT * FROM one WHERE sex = ?1 group by id</query>
        </named-native-query>

    <!--result-class用来指定实体类,result-set-mapping用来指定映射的名称-->
        <named-native-query name="One.findUserByPrimaryKey1" result-class="cn.com.bmsoft.stormplan.basic.entity.One">
            <query>SELECT * FROM one WHERE id = :id</query>
        </named-native-query>
    </entity-mappings>

    Repository层代码展示

    package cn.com.bmsoft.stormplan.basic.dao;

    import cn.com.bmsoft.stormplan.basic.entity.One;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    import org.springframework.data.repository.query.Param;

    import java.util.List;

    public interface OneRepository extends JpaRepository<One, Integer>{
        //1.2 XML定义 原生sql查询 named-native-query
        List<One> selectByName1(String sex);
        One findUserByPrimaryKey1(@Param("id")Integer id);
    }

    4. SpEL表达式

    Spring Data JPA 1.4版本开始,支持在@Query定义的查询中使用SpEL模板表达式。在执行查询的时候,这些表达式通过一个事先定义好的变量集合求值。Spring Data JPA支持一个名为entityName的变量。它的用法是select x from #{#entityName} x。它会将域类型的entityName与给定repository关联起来。entityName按照如下方式处理:如果域类型在@Entity注解上设置了name属性,则使用该属性。否则直接使用域类型的类名。

    下例演示了在定义带有查询方法和手工定义查询的repository接口时使用#{#entityName}表达式。

    public interface UserRepository extends JpaRepository<User, Integer>{
        @Query(value = "SELECT u FROM #{#entityName} u WHERE u.name like %:name% and u.sex = :sex")
        List<User> findTable4(@Param("name") String name, @Param("sex")String sex);
    }

    SpEL表达式的好处:

    参考网址:https://www.cnblogs.com/tilv37/p/6944182.html

    5. Specifications动态构建查询

    5.1. 参数介绍

    1. Predicate:单独每一条查询条件的详细描述

    Predicate[]:多个查询条件的详细描述

    1. Root:查询哪个表
    2. CriteriaQuery:查询哪些字段,排序是什么
    3. CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每个查询条件都是什么方式

    1CriteriaQuery<T>:主要是构建查询条件

    distinct、select、where、groupby、having、orderby等

    2CriteriaBuilder:主要是用来进行一些函数操作

         ① and

         ② or

         ③ between

         ④ lt(小于)、le(小于等于)、gt(大于)、ge(大于等于)

         ⑤ not(非)等...

    5.2. 代码案例

    //where empname like ?  group by wages

    @Override
    public Page<EmployeeEntity> search1(EmployeeVO employeeVO) throws Exception {
       Pageable pageable = PageRequest.of(employeeVO.getPage() - 1, employeeVO.getSize(), Sort.Direction.ASC,"empid");
       Specification<EmployeeEntity> specification = new Specification<EmployeeEntity>() {
          @Override
          public Predicate toPredicate(Root<EmployeeEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
             List<Predicate> list = new ArrayList<>(10);
             if(StringUtils.isNotBlank(employeeVO.getEmpname())){
                list.add(cb.and(
                      cb.like(root.get("empname").as(String.class),"%"+ employeeVO.getEmpname()+"%")
                ));
             }
             Expression<BigDecimal> wages = root.get("wages").as(BigDecimal.class);
             return criteriaQuery.where(list.toArray(new Predicate[list.size()])).groupBy(wages).getRestriction();
             //where empname like ?  group by wages
          }
       };
       Page<EmployeeEntity> stuPage = employeeRepository.findAll(specification, pageable);
       return stuPage;
    }

    //where empname like ? or deptid=?

    @Override
    public Page<EmployeeEntity> search2(EmployeeVO employeeVO) throws Exception {
       Pageable pageable = PageRequest.of(employeeVO.getPage() - 1, employeeVO.getSize(), Sort.Direction.ASC,"empid");
       Specification<EmployeeEntity> specification = new Specification<EmployeeEntity>() {
          @Override
          public Predicate toPredicate(Root<EmployeeEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
             List<Predicate> list = new ArrayList<>(10);
             if(StringUtils.isNotBlank(employeeVO.getEmpname()) && null != employeeVO.getDeptid()){
                list.add(cb.or(
                      cb.like(root.get("empname").as(String.class),"%"+ employeeVO.getEmpname()+"%"),
                      cb.equal(root.get("deptid").as(Integer.class), employeeVO.getDeptid())
                ));
             }//where empname like ? or deptid=?
             return criteriaQuery.where(list.toArray(new Predicate[list.size()])).getRestriction();
          }
       };
       Page<EmployeeEntity> stuPage = employeeRepository.findAll(specification, pageable);
       return stuPage;
    }

    //where wages < ?

    @Override
    public Page<EmployeeEntity> search3(EmployeeVO employeeVO) throws Exception {
       Pageable pageable = PageRequest.of(employeeVO.getPage() - 1, employeeVO.getSize(), Sort.Direction.ASC,"empid");
       Specification<EmployeeEntity> specification = new Specification<EmployeeEntity>() {
          @Override
          public Predicate toPredicate(Root<EmployeeEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
             Predicate wages = cb.lt(root.get("wages").as(BigDecimal.class), employeeVO.getWages());
             return criteriaQuery.where(wages).getRestriction();
          }
       };//where wages < ?
       Page<EmployeeEntity> stuPage = employeeRepository.findAll(specification, pageable);
       return stuPage;
    }

    //where wages between ? and ?

    @Override
    public Page<EmployeeEntity> search4(EmployeeVO employeeVO){
       Pageable pageable = PageRequest.of(employeeVO.getPage() - 1, employeeVO.getSize(), Sort.Direction.ASC,"empid");
       Specification<EmployeeEntity> specification = new Specification<EmployeeEntity>() {
          @Override
          public Predicate toPredicate(Root<EmployeeEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
             Predicate wages = cb.between(root.get("wages").as(BigDecimal.class), employeeVO.getMinWages(), employeeVO.getMaxWages());
             return criteriaQuery.where(wages).getRestriction();
          }
       };//where wages between ? and ?
       Page<EmployeeEntity> stuPage = employeeRepository.findAll(specification, pageable);
       return stuPage;
    }

    6. JPA Join联表查询

    构建表关系如下:

     

    实体类代码展示

    @Entity
    @Table(name="a")
    public class AEntity {
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="aid",nullable=false)
        private Integer aid;

        @Column(name="aname",nullable=false)
        private String aname;

    省略set、get方法
    }

    @Entity
    @Table(name="b")
    public class BEntity {
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="bid",nullable=false)
        private Integer bid;

        @Column(name="bname",nullable=false)
        private String bname;

        @OneToOne(cascade=CascadeType.ALL) //B是关系的维护端,当删除 b,会级联删除 a
        @JoinColumn(name = "aid", referencedColumnName = "aid") //name:关联id,referencedColumnName:A表id
        private AEntity aEntity;//A表

    省略set、get方法
    }

    @Entity
    @Table(name="c")
    public class CEntity {
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="cid",nullable=false)
        private Integer cid;

        @Column(name="cname",nullable=false)
        private String cname;
        省略set、get方法

    }

    @Entity
    @Table(name="d")
    public class DEntity {
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="did",nullable=false)
        private Integer did;

        @Column(name="dname",nullable=false)
        private String dname;

        //可选属性optional=false,表示B不能为空。删除d,不影响b   @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
        @JoinColumn(name = "bid", referencedColumnName = "bid")//name:关联id,referencedColumnName:B表id
        private BEntity bEntity;//B表

        //可选属性optional=false,表示C不能为空。删除d,不影响c
    @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)
        @JoinColumn(name = "cid", referencedColumnName = "cid")//name:关联id,referencedColumnName:C表id
        @JsonBackReference
        private CEntity cEntity;//C表
        省略set、get方法
    }

    Service层代码展示

    @Override
    public Page<DEntity> search(DVO dVO) {
        Pageable pageable = PageRequest.of(dVO.getPage() - 1, dVO.getSize(), Sort.Direction.ASC,"did");
        Specification<DEntity> specification = new Specification<DEntity>() {
            @Override
            public Predicate toPredicate(Root<DEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                List<Predicate> list = new ArrayList<>(10);

                //A:B:D 1:1:n  根据D的实体类查询aname
                if (StringUtils.isNotBlank(dVO.getAname())) {
                    Join<AEntity,DEntity> ajoin = root.join("bEntity",JoinType.LEFT);
                    list.add(cb.like(ajoin.get("aEntity").get("aname").as(String.class),"%"+ dVO.getAname()+"%"));
                }//select * from d left join b on d.bid=b.bid left join a on b.aid=a.aid where a.aname like "%xxx%"

                //B:D 1:n  根据D的实体类查询bname
                if (StringUtils.isNotBlank(dVO.getBname())) {
                    Join<BEntity,DEntity> ajoin = root.join("bEntity",JoinType.LEFT);
       list.add(cb.like(ajoin.get("bname").as(String.class),"%"+ dVO.getBname()+"%"));
    }//select * from d left join b on d.bid=b.bid where b.bname like "%xxx%"

                //C:D 1:n  根据D的实体类查询cname
                if (StringUtils.isNotBlank(dVO.getCname())) {
                    Join<BEntity,DEntity> ajoin = root.join("cEntity",JoinType.LEFT);
                    list.add(cb.like(ajoin.get("cname").as(String.class),"%"+ dVO.getCname()+"%"));
                }//select * from d left join c on d.cid=c.cid where c.cname like "%xxx%"

                return criteriaQuery.where(list.toArray(new Predicate[list.size()])).getRestriction();
            }
        };
        Page<DEntity> stuPage = dRepository.findAll(specification, pageable);
        return stuPage;
    }

  • 相关阅读:
    js中常用的算法排序
    bootstrap Table的使用方法
    js中的继承
    js函数的节流与防抖
    along.js
    Vue组件通讯
    前端性能优化
    Vue路由学习心得
    Vue 2.0 路由全局守卫
    【前端】自适应布局方法总结
  • 原文地址:https://www.cnblogs.com/supiaopiao/p/10901793.html
Copyright © 2011-2022 走看看