Spring Data JPA使用

本文从以下几个方面介绍Spring Data JPA使用

1、Spring Data JPA环境搭建;

2、简单增删改查;

3、使用Example对象查询

4、jpql查询;

5、Specification实现单表单、多表动态查询;

6、排序、分页查询

7、Specification实现多表分页排序动态查询;

8. EntityManager实现结果集封装自定义实体类

1. Spring Data JPA环境搭建

 1.spring data jpa(基于hibernate框架对JPA的实现、对接数据库为MYSQL、连接池使用druid)开发步骤
1.引入spring-data-jpa相关jar、spring相关jar、spring对ORM框架支持jar、hibernate相关jar、hibernate对JPA实现的相关jar、MYSQL数据驱动、druid连接池以及junit测试Jar;

<dependencies>
        <!--spring-data-jpa 需要引⼊的jar,start-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.1-b04</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.6</version>
        </dependency>
        <!--spring-data-jpa 需要引⼊的jar,end-->
        <!--spring 相关jar,start-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <!--spring对orm框架的⽀持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <!--spring 相关jar,end-->
        <!--hibernate相关jar包,start-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.0.Final</version>
        </dependency>
        <!--hibernate对jpa的实现jar-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.0.Final</version>
        </dependency>
        <!--hibernate相关jar包,end-->
        <!--mysql 数据库驱动jar-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!--spring-test-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <!--单元测试jar-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


2.在resources下增加spring核心配置文件
spring核心配置文件配置以下信息
2.1 在jdbc.properties文件中配置数据库连接信息,在spring文件中引入并增加数据库连接池对象

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test-transfer
jdbc.username=root
jdbc.password=123456
    <!--    引入数据库连接信息 jdbc.properties-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--    创建数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

2.2 配置JPA中 entityManagerFactory对象(类似 Mybatis中SqlSessionFactory对象;)
给entityManagerFactory对象配置数据源、待扫描的实体类包路径、jpa的具体实现HibernatePersistenceProvider、JPA方言HibernateJpaDialect、
适配器HibernateJpaVendorAdapter等属性;以及指定适配器HibernateJpaVendorAdapter 具体细节,包含 指定数据库类型MySql、是否自动建表、
是否打印SQl语句、指定数据库方言MySQLDialect

<!--    创建entityManagerFactory 对象-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--        配置数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--        配置数据库表对应的实体类所在对象包扫描-->
        <property name="packagesToScan" value="com.kay.pojo"/>
        <!--        指定JPA的具体实现-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
        </property>
        <!--        指定JPA的方言-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
        </property>
        <!--        指定适配器-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--                指定hibernate适配器的具体细节-->
                <!--                指定数据库类型 此处指定为MySQl-->
                <property name="database" value="MYSQL"/>
                <!--                配置数据库中表是否自动创建 (启动时如果实体类对应的数据库表没有创建,是否交由框架创建对应的表)-->
                <property name="generateDdl" value="false"/>
                <!--                是否显示SQl 执行语句-->
                <property name="showSql" value="true"/>
                <!--                指定数据库方言-->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
            </bean>
        </property>
    </bean>


    <!--  对比Mybatis:   sqlSessionFactory由spring容器生成单例对象-->
    <!--    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">-->
    <!--        &lt;!&ndash;别名映射扫描&ndash;&gt;-->
    <!--        <property name="typeAliasesPackage" value="com.kay.pojo"/>-->
    <!--        &lt;!&ndash;数据源dataSource&ndash;&gt;-->
    <!--        <property name="dataSource" ref="dataSource"/>-->
    <!--    </bean>-->

2.3 配置事务管理器 JpaTransactionManager(区别与JdbcTemplate和Mybatis的DataSourceTransactionManager) ,注入 entityManagerFactory 属性

<!--    配置事务管理器
    注意:
        jdbcTemplate和MyBatis 使用的是DataSourceTransactionManager
-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<!--        引入transactionManager属性对象-->
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

2.4 引入jpa约束头 ; 配置dao接口扫描包、引入 entityManagerFactory 对象 及 事务管理器对象

xmlns:jpa="http://www.springframework.org/schema/data/jpa"
....
http://www.springframework.org/schema/data/jpa  http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
<!--    配置dao接口扫描包、引入 entityManagerFactory 对象 及 事务管理器对象-->
    <jpa:repositories base-package="com.kay.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/>

完整的配置信息

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/data/jpa  http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
">

    <!--    引入数据库连接信息 jdbc.properties-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--    创建数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--    创建entityManagerFactory 对象-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--        配置数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--        配置数据库表对应的实体类所在对象包扫描-->
        <property name="packagesToScan" value="com.kay.pojo"/>
        <!--        指定JPA的具体实现-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
        </property>
        <!--        指定JPA的方言-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
        </property>
        <!--        指定适配器-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--                指定hibernate适配器的具体细节-->
                <!--                指定数据库类型 此处指定为MySQl-->
                <property name="database" value="MYSQL"/>
                <!--                配置数据库中表是否自动创建 (启动时如果实体类对应的数据库表没有创建,是否交由框架创建对应的表)-->
                <property name="generateDdl" value="false"/>
                <!--                是否显示SQl 执行语句-->
                <property name="showSql" value="true"/>
                <!--                指定数据库方言-->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
            </bean>
        </property>
    </bean>


    <!--  对比Mybatis:   sqlSessionFactory由spring容器生成单例对象-->
    <!--    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">-->
    <!--        &lt;!&ndash;别名映射扫描&ndash;&gt;-->
    <!--        <property name="typeAliasesPackage" value="com.kay.pojo"/>-->
    <!--        &lt;!&ndash;数据源dataSource&ndash;&gt;-->
    <!--        <property name="dataSource" ref="dataSource"/>-->
    <!--    </bean>-->


<!--    配置事务管理器
    注意:
        jdbcTemplate和MyBatis 使用的是DataSourceTransactionManager
-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<!--        引入transactionManager属性对象-->
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

<!--    配置dao接口扫描包、引入 entityManagerFactory 对象 及 事务管理器对象-->
    <jpa:repositories base-package="com.kay.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/>

<!--    配置spring的包扫描-->
    <context:component-scan base-package="com.kay"/>
</beans>

2. 实体类注解配置 (javax.persistence下的注解)
1. @Entity 实体类和表的映射,@Table(name = "xxx")指定表名(不配置时默认实体类名小写)
2. @Id配置主键 
 @GeneratedValue(strategy = GenerationType.IDENTITY) 配置主键生成策略 (自增)
 默认为 GenerationType.AUTO 
 GenerationType.SEQUENCE:依靠序列来产生主键(例如Oracle数据库)
 @Column 属性值和表字段映射关系不配置默认为字段名等于属性名

@Entity
@Table(name = "tb_account")
public class TbAccount {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String cardNo;
    private String name;
    private int money;
    
       .....
}

3. dao接口类继承JpaRepository<T, ID>   (<TbAccount, Integer> ) T:当前对应的实体类 ID实体类对应表主键字段类型

public interface TbAccountDao extends JpaRepository<TbAccount, Integer> , JpaSpecificationExecutor<TbAccount>{ 
... 
}

2、简单增删改查; 

1. 直接通过dao接口类,调用当前类对应的表增删改查

2.在dao接口中可以按照JPA指定的语法,编写特定的接口方法,spring data jpa底层将实现对应的SQl;

findBy + 属性名 + 对应条件 

deleteBy + 属性名 + 对应条件

..... 

3、使用Example对象操作

通过Example对象封装条件

import org.springframework.data.domain.Example;
TbAccount tbAccount = new TbAccount();
tbAccount.setName("kay");
Example<TbAccount> example = Example.of(tbAccount);

将获取到Example的对象,传入接口中

4、jpql查询;

 在dao接口类中,在对应的接口方法上面,使用@Query 注解编写自定义SQl语句 

1. 在sql中可以加上select * 也可以直接from开头 SQL中表名及字段信息,均为实体类名及字段名

2. 可以按照方法参数顺序指定参数 ?1 ?2 传值  ; 也可以在方法参数上通过@Param("name")注解定义参数名称,通过 :name 的形式传值

@Query(value = "from TbAccount where name = ?1 and  money = ?2")
List<TbAccount> findByJPQL(String name, Integer money); 
@Query(value = "from TbAccount where name = :name and  money = :money")
List<TbAccount> findByJPQLInParamsName(@Param("name")String name, @Param("money")Integer money);

3. 使用原生SQL语句  

@Query注解中 nativeQuery 属性指定SQLl是否为原生,默认为false,指定为true时,value中sql语句为原生sql 此时表名、字段名均为数据库中表及字段真实信息

@Query(value = "select * from tb_account where name = :name and  money = :money", nativeQuery = true)
List<TbAccount> findByJPQLInNative(@Param("name")String name, @Param("money")Integer money);

5、Specification实现单表,多表动态查询;

 1. 通过内部类创建Specification 对象 root 中获取需要查询对象的属性;criteriaBuilder根据条件(or and like 大于,小于等)返回Predicate 对象

Specification<TbAccount> specification = new Specification<TbAccount>() {
    /**
     *
     * @param root 需要查询对象的属性
     * @param criteriaQuery
     * @param criteriaBuilder 构造查询条件
     * @return
     */
    @Override
    public Predicate toPredicate(Root<TbAccount> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        Path<Object> name = root.get("name");
        Predicate predicate1 = criteriaBuilder.equal(name, "kay");


        Path<Object> money = root.get("money");
        Predicate predicate2 = criteriaBuilder.gt(money.as(Integer.class), 1); 
        //条件1 predicate1  、 条件2 predicate2 使用 or 关联
        Predicate predicate = criteriaBuilder.or(predicate1, predicate2);
        return predicate;
    }
};

List<TbAccount> all = tbAccountDao.findAll(specification);

2. 多表关联动态查询 以用户 及 用户订单表为例 (@JoinColumn 中name指此@JoinColumn 注解配置的实体类对应的字段,referencedColumnName指当前实体类字段  )

@Table(name = "user")
@Entity
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String username;
    private String password;
    private String birthday;

    @OneToMany
    @JoinColumn(name="uid",referencedColumnName = "id")
    private List<Order> orderList; // 用户对应订单可为多个 一对多

}

@Entity
@Table(name = "orders")
public class Order implements Serializable {

    @Id
    private Integer id;
    private String orderTime;
    private Double total;
    private Integer uid;
}

  查询用户Id及订单金额大于特定数字 使用 root.join("orderList") 获取到Order对象,关联对应的total字段

public Specification<User> findByCondition(Integer userId, Integer money){
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
           // 动态封装  Predicate 列表
            List<Predicate> predicateList = new ArrayList<Predicate>();
            if (userId != null){
                Path<Object> idPath = root.get("id");
                predicateList.add(criteriaBuilder.equal(idPath, userId));
            }
            if (money != null){
                Path<Object> moneyPath = root.join("orderList").get("total");
                predicateList.add(criteriaBuilder.gt(moneyPath.as(Integer.class), money));
            }
            Predicate[] result = new Predicate[predicateList.size()];
            criteriaQuery.where(predicateList.toArray(result));
            return criteriaQuery.getRestriction();
        }
    };
} 

@Test
public void test4(){
//  查询订单金额大于 3000  的用户
  Specification<User> specification= tbAccountService.findByCondition(null, 3000);
  userDao.findAll(userSpecification);
// 查询订单金额大于 3000 且用户id为 1 的用户
 Specification<User> specification= tbAccountService.findByCondition(1, 3000); 
 userDao.findAll(userSpecification);
}

 3. 多对多动态查询

用户,角色 及 用户角色三种表  sys_user_role(userid, orderid) user(id) role(id)

使用 @JoinTable关联中间表 

@Table(name = "user")
@Entity
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    .....

    @ManyToMany
    @JoinTable(name = "sys_user_role", //中间表
        joinColumns = {@JoinColumn(name = "userid", referencedColumnName="id")},
            inverseJoinColumns = {@JoinColumn(name = "roleid", referencedColumnName = "id")})
    private List<Role> roleList;
}

public Specification<User> findUserRoleByCondition(Integer userId, Integer money, Integer roleId){
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            List<Predicate> predicateList = new ArrayList<Predicate>();
            if (userId != null){
                Path<Object> idPath = root.get("id");
                predicateList.add(criteriaBuilder.equal(idPath, userId));
            }
            if (money != null){
                Path<Object> moneyPath = root.join("orderList").get("total");
                predicateList.add(criteriaBuilder.gt(moneyPath.as(Integer.class), money));
            }

            if (roleId != null){
                Path<Object> moneyPath = root.join("roleList").get("id");
                predicateList.add(criteriaBuilder.equal(moneyPath.as(Integer.class), roleId));
            }

            Predicate[] result = new Predicate[predicateList.size()];
            criteriaQuery.where(predicateList.toArray(result));
            return criteriaQuery.getRestriction();
        }
    };
}

6、排序、分页查询

排序:创建Sort对象传入相应方法即可

Sort sort = new Sort(Sort.Direction.DESC, "id"); // 按照ID字段降序

分页:创建PageRequest对象     传入前几节介绍的方法中即可

PageRequest pageRequest = PageRequest.of(0, 2); // 0 当前页  2是页数

7、Specification实现多表分页排序动态查询 

Specification内部类中通过criteriaQuery设置排序信息; 调用findAll传入Specification查询时,传入PageRequest分页对象

public Specification<User> findByCondition(Integer userId, Integer money){
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            List<Predicate> predicateList = new ArrayList<Predicate>();
            if (userId != null){
                Path<Object> idPath = root.get("id");
                predicateList.add(criteriaBuilder.equal(idPath, userId));
            }
            if (money != null){
                Path<Object> moneyPath = root.join("orderList").get("total");
                predicateList.add(criteriaBuilder.gt(moneyPath.as(Integer.class), money));
            }
            Predicate[] result = new Predicate[predicateList.size()];
            criteriaQuery.where(predicateList.toArray(result));
                // 根据id降序
            criteriaQuery.orderBy(criteriaBuilder.desc(root.get("id")));
            return criteriaQuery.getRestriction();
        }
    };
}
@Test
public void test5(){
    Specification<User> userSpecification = tbAccountService.findByCondition(null, 0);
    PageRequest pageRequest = PageRequest.of(0, 2);
    Page<User> all = userDao.findAll(userSpecification, pageRequest);
    List<User> content = all.getContent();
    System.out.println("all.getTotalPages() " + all.getTotalPages());
}

8. EntityManager实现结果集封装自定义实体类 

 1.定义返回结果集实体类(生成构造方法)

public class OrderUserDto {
    private Integer orderId;
    private String orderTime;
    private Double total;
    private Integer userId;
    private String username;

    public OrderUserDto(Integer orderId, String orderTime, Double total, Integer userId, String username) {
        this.orderId = orderId;
        this.orderTime = orderTime;
        this.total = total;
        this.userId = userId;
        this.username = username;
    }
}

2. 接口对应实体类,引入@PersistenceContext注解,注入  EntityManager 对象

编写对应方法及实现 其中使用select new com.kay.dto.OrderUserDto(按照实体类有参构造的对应字段顺序,匹配数据库表对应字段值,将实现实体类的自动封装)

PS: Query query = entityManager.createQuery(sql); 使用的是实体类、属性编写;当entityManager.createNaticeQuery时,sql中需要编写的是原生SQL

@Component
public class OrderSQL {
    @PersistenceContext
    private EntityManager entityManager;

    // Integer orderId, String orderTime, Double total, Integer userId, String username
    public List<OrderUserDto> findOrderUserByUserId(Integer userId, Integer page, Integer pageSize){
        String sql = "select new com.kay.dto.OrderUserDto(o.id, o.orderTime, o.total, u.id, u.username) " +
                " from Order o left join User u on o.uid = u.id where u.id = :userId order by o.orderTime desc";
        Query query = entityManager.createQuery(sql);
        query.setParameter("userId", userId);
        // 分页
        int startWith = page * pageSize;
        query.setFirstResult(startWith);//从第多少条开始获取数据
        query.setMaxResults(pageSize);//共取多少条数据

        return query.getResultList();
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值