Spring与SpringBoot整合Spring Data JPA及使用

本节由浅入深,再浅出学习Spring data JPA。我的学习路程是先通过spring整合Spring data JPA来具体学习,逐渐深入,学习完这些重要知识点后,再浅出到使用SpringBoot来整合Spring data JPA。

一.Spring整合Spring Data JPA

Spring Data JPA是Spring Data项目下的一个模块。提供了一套基于JPA标准操作数据库的简化方案,底层默认是依赖Hibernate JPA来实现的。

Spring Data JPA的技术特点:我们只需要定义接口并继承Spring Data JPA中所提供的接口就可以了。不需要编写接口实现类。

1.创建Spring Data JPA的项目,导入依赖,编写配置文件

    <dependencies>
    <!--Spring Ioc相关依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.18</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.18</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.18</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>5.3.18</version>
    </dependency>

    <!--Spring Aop的相关依赖-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.8.RC2</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.18</version>
    </dependency>

    <!--Spring jdbc的相关依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.18</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.3.18</version>
    </dependency>

    <!--spring orm的相关依赖-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.3.18</version>
    </dependency>

    <!--单元测试用的依赖-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.18</version>
    </dependency>

    <!--日志依赖-->
    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>

    <!--Hibernate的核心依赖,9个必须要导入-->
    <!-- https://mvnrepository.com/artifact/antlr/antlr -->
    <dependency>
        <groupId>antlr</groupId>
        <artifactId>antlr</artifactId>
        <version>2.7.7</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
    <dependency>
        <groupId>org.dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>2.1.3</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.geronimo.specs/geronimo-jta_1.1_spec -->
    <dependency>
        <groupId>org.apache.geronimo.specs</groupId>
        <artifactId>geronimo-jta_1.1_spec</artifactId>
        <version>1.1.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.hibernate.common/hibernate-commons-annotations -->
    <dependency>
        <groupId>org.hibernate.common</groupId>
        <artifactId>hibernate-commons-annotations</artifactId>
        <version>5.1.2.Final</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.6.5.Final</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.hibernate.javax.persistence/hibernate-jpa-2.1-api -->
    <dependency>
        <groupId>org.hibernate.javax.persistence</groupId>
        <artifactId>hibernate-jpa-2.1-api</artifactId>
        <version>1.0.0.Final</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.jboss/jandex -->
    <dependency>
        <groupId>org.jboss</groupId>
        <artifactId>jandex</artifactId>
        <version>2.0.0.Final</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.28.0-GA</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.jboss.logging/jboss-logging -->
    <dependency>
        <groupId>org.jboss.logging</groupId>
        <artifactId>jboss-logging</artifactId>
        <version>3.4.1.Final</version>
    </dependency>

    <!--mysql数据驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.25</version>
    </dependency>

    <!--连接池相关的依赖-->
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.5</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-c3p0 -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-c3p0</artifactId>
        <version>5.6.7.Final</version>
    </dependency>

    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>mchange-commons-java</artifactId>
        <version>0.2.19</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>5.6.7.Final</version>
    </dependency>

    <!--Spring Data JPA的相关依赖-->

    <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-commons -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-commons</artifactId>
        <version>2.6.3</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>2.6.3</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.36</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.28</version>
    </dependency>

</dependencies>

applicationContext.xml相比之前spring整合Hibernate JPA时多了一个jpa的命名空间,其他也不用删除先,然后添加一个jpa的dao扫描,具体配置如下:

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

    <!--配置读取properties文件的工具类-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置c3p0数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="driverClass" value="${jdbc.driver.class}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--Spring整合 Hibernate JPA ,配置EntityManagerFactory-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!--hibernate相关属性的注入:-->
                <!--database配置数据库类型-->
                <property name="database" value="MYSQL"/>
                <!--开启正向工程,自动创建表-->
                <property name="generateDdl" value="true"/>
                <!--开启显示执行的sql-->
                <property name="showSql" value="true"/>
            </bean>
        </property>
        <!--配置要扫描的实体的包-->
        <property name="packagesToScan">
            <list>
                <value>com.haiexijun.pojo</value>
            </list>
        </property>
    </bean>

    <!--配置Hibernate的事务管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>


    <!--开启注解事务处理-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!--配置springIOC的注解扫描-->
    <context:component-scan base-package="com.haiexijun"/>

    <!--Spring Data JPA的配置-->
    <!--base-package:扫描dao接口所在的包-->
    <jpa:repositories base-package="com.haiexijun.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/>

</beans>

jdbc.properties

jdbc.url=jdbc:mysql://localhost:3306/hibernate02
jdbc.driver.class=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=zc20020106

2.创建dao继承JpaRepository就好了,不用去写任何CRUD的接口的实现。

package com.haiexijun.dao;

import com.haiexijun.pojo.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface usersDao extends JpaRepository<Users,Integer> {

}

下面是一些测试代码:

import com.haiexijun.dao.usersDao;
import com.haiexijun.pojo.Users;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testDao {
    @Autowired
    private usersDao usersDao;

    @PersistenceContext(name = "entityManagerFactory")
    private EntityManager entityManager;

    // 测试spring data jpa的环境
    @Test
    @Transactional
    @Rollback(value = false)
    public void testInsertUser(){
        Users users=new Users();
        users.setUserName("李四");
        users.setUserAge(23);
        usersDao.save(users);
    }

    //测试usersDao注入的到底是什么
    @Test
    public void test01(){
        System.out.println(usersDao);
        //org.springframework.data.jpa.repository.support.SimpleJpaRepository@5f3b84bd
        System.out.println(usersDao.getClass());
        //class com.sun.proxy.$Proxy59
        //会发现注入的是一个代理对象

        System.out.println("======");
        JpaRepositoryFactory factory=new JpaRepositoryFactory(entityManager);
        //factory.getRepository()方法可以帮助我们为接口生成实现类,而这个实现类是SimpleJpaRepository的代理对象
        //接口必须要继承Repository接口
        usersDao ud = factory.getRepository(com.haiexijun.dao.usersDao.class);
        System.out.println(ud);
        System.out.println(ud.getClass());
    }

}

3.Repository接口详解

Repository接口是Spring Data JPA中为我们提供的所有接口中的顶层接口

repository提供了两种查询方式的支持:

(1)基于方法名称的命名规则查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tYoKFtKd-1651581569560)(./assets/1651575749764-image.png)]

规则是什么?
findBy+属性名称(属性名称的首写字母要大写)+查询条件(首字母要大写)
具体更多的规则查看Spring官网即可。

userDao02:

package com.haiexijun.dao;

import com.haiexijun.pojo.Users;
import org.springframework.data.repository.Repository;

import java.util.List;


@org.springframework.stereotype.Repository
public interface userDao02 extends Repository<Users,Integer> {

     List<Users> findByUserNameIs(String name);

     List<Users> findByUserNameLike(String name);

    List<Users> findByUserNameAndUserAgeGreaterThanEqual(String name, Integer age);
}

测试用例:

    // 根据姓名查询数据
    @Test
    @Transactional
    @Rollback(value = false)
    public void test02(){
        //判断相等的条件有三种方式
        //1.什么都不写 2.is 3.equal
        List<Users> list= userDao02.findByUserNameIs("张三");
        System.out.println(list);
    }

    //根据用户姓名Like处理
    @Test
    @Transactional
    @Rollback(value = false)
    public void test03(){
        List<Users> list= this.userDao02.findByUserNameLike("张%");
        System.out.println(list);
    }

    //查询名称为王五,并且他的年龄大于等于20岁
    @Test
    @Transactional
    @Rollback(value = false)
    public void test04(){
        List<Users> list=userDao02.findByUserNameAndUserAgeGreaterThanEqual("李四",20);
        System.out.println(list);
    }

(2)基于@Query注解查询

也有如下两种方式:

通过JPQL语句查询:

JPQL是通过Hibernate的HQL演变过来的。他和HQL语法极其相似。

    //使用@Query注解
    // 可以用?index,index从1开始来传参。如:from Users where userName=?1
    //也可以用下面这种@param来传参数
    @Query(value = "from Users where userName=:name")
    List<Users> queryUsersByUserNameUseJPQL(@Param("name") String name);

    @Query(value = "from Users where userName like :name")
    List<Users> queryUsersByNameLikeUseJPQL(@Param("name") String name);

    @Query("from Users where userName=?1 and userAge>?2")
    List<Users> queryByUserNameAndUserAgeGreaterThanEqualUseJPQL(String name, int age);

@Query注解还可以通过sql语句来查询:

    // 使用@Query注解查询SQL语句
    //要开启nativeQuery为true
    @Query(value = "select * from t_users where username=? ",nativeQuery = true)
    List<Users> queryUsersByNameUseSQL(String name);

    @Query(value = "select * from t_users where username like ?",nativeQuery = true)
    List<Users> queryUsersByNameLikeUseSQL(String name);

    @Query(nativeQuery = true,value = "select * from t_users where username=? and userage>?")
    List<Users> queryByUserNameAndUserAgeGreaterThanEqualUseSQL(String name, int age);

(3)下面演示@Query注解的更新操作:
jpql编写 DAO接口update语句 还要添加@Modifying注解:

    //通过@Query注解进行数据更新
    //jpql编写 DAO接口update语句 要添加@Modifying注解
    @Query("update Users  set userAge =?2 where userId =?1 ")
    @Modifying
    void updateUserAgeById(Integer id,Integer age);

4.PagingAndSortingRepository接口

这个接口主要是帮助我们完成分页和排序处理。

(1)分页处理

我们创建一个新的Dao接口,继承至PagingAndSortingRepository接口。

我们点开这个接口的源代码查看后发现,这两个接口有2个重载的方法。这两个方法需要传入不同的参数,一个需要Sort类型的参数(用于排序),一个需要Pageable类型的参数(用于分页)。并且我们也会发现这两个方法的名称都叫做findAll(),也就是说,是对数据库表当中的所有的数据进行查询的。

package com.haiexijun.dao;

import com.haiexijun.pojo.Users;
import org.springframework.data.repository.PagingAndSortingRepository;

//继承自PagingAndSortingRepository这个接口,并且这个接口里面要传入一个泛型,泛型的一个个参数是要操作的表对应的实体类,第二个参数为表主键的数据类型
public interface UserDao01 extends PagingAndSortingRepository<Users,Integer> {
  
}

如果你比较懒,不想再创建一个人接口,也没有关系,因为我们之前用的UsersDao就是就继承自JPArepository接口,而JPArepository接口又继承了PagingAndSortingRepository这个接口,也就是说你可以用之前得Dao就可以了。

然后回到我们的测试代码当中。编写如下的测试方法来学习这个接口的相关操作。

比如说我要对数据表中所有的数据做分页处理

    /**
     * 分页
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test12(){
        //这里注意,Pageable是接口,我们不能直接new这个接口,而应该new他的接口实现类PageRequest。
        Integer page=0;// page:当前页得索引,丛林开始
        Integer size=3;// size:每页显示几条数据,这里是三条
        Pageable pageable =PageRequest.of(page,size);
        Page<Users> p= usersDao.findAll(pageable);
        System.out.println("数据得总条数:"+p.getTotalElements());
        System.out.println("数据的总页数:"+p.getTotalPages());
        List<Users> list=p.getContent();
        System.out.println("分页得结果:"+list);
    }

(2)排序处理

    /**
     * 排序
     * 对单列做排序处理
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test13(){
        // Sort对象封装了排序规则以及指定的排序字段(用对象的属性名来表示)
        //Sort构造方法可以传入两个参数
        //第一个参数direction:排序规则
        //第二个参数properties:指定做排序的属性
        Sort sort=Sort.by(Sort.Direction.DESC,"userId");
        List<Users> list=usersDao.findAll(sort);
        System.out.println(list);
    }

    /**
     * 排序
     * 对多列做排序处理
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test14(){
        // Sort对象封装了排序规则以及指定的排序字段(用对象的属性名来表示)
        //Sort构造方法可以传入两个参数
        //第一个参数direction:排序规则
        //第二个参数properties:指定做排序的属性
        //Order的构造参数与Sort的一样
        Sort.Order order1=new Sort.Order(Sort.Direction.DESC,"userAge");
        Sort.Order order2=new Sort.Order(Sort.Direction.ASC,"userId");
        Sort sort=Sort.by(order1,order2);
        List<Users> list=usersDao.findAll(sort);
        System.out.println(list);
    }

5.JpaSpecificationExecutor接口

这个接口支持多条件查询,同时支持分页与排序。

看下图,会发现JpaSpecificationExecutor接口并没有继承自任何的接口。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Czodml9e-1651581569562)(./assets/1651547426530-image.png)]

我们要新新建一个dao:

package com.haiexijun.dao;

import com.haiexijun.pojo.Users;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;


//继承自JpaSpecificationExecutor接口,这个接口传入一个泛型(要查询的表对应的实体类)。
//这个接口不能单独使用,需要配合着jpa中的其他接口一起使用
@Repository
public interface UserDao01 extends JpaSpecificationExecutor<Users>, JpaRepository<Users,Integer> {

}

(1)单条件查询:

    /**
     * 需求:根据用户姓名查询数据
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test15(){
        Specification<Users> spec=new Specification<Users>() {
            /**
             *
             * @param root :根对象。封装了查询条件的对象
             * @param query :定义了基本的查询,一般不使用
             * @param criteriaBuilder :创建一个查询的条件
             * @return Predicate:定义了查询条件
             */
            @Override
            public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Predicate pre= criteriaBuilder.equal(root.get("userName"),"张三");
                return pre;
            }
        };
        List<Users> list= userDao01.findAll(spec);
        System.out.println(list);
    }

(2)多条件查询

方式一:

    /**
     * 多条件查询 方式一
     *
     * 需求,要求根据用户的姓名以及年龄查询数据
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test16(){
        Specification<Users> spec=new Specification<Users>() {
            @Override
            public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> list=new ArrayList<Predicate>();
                list.add(criteriaBuilder.equal(root.get("userName"),"张三"));
                list.add(criteriaBuilder.equal(root.get("userAge"),26));
                //有几个条件就传入几个predicate对象
                Predicate[] arr=new Predicate[list.size()];
                return criteriaBuilder.and(list.toArray(arr));
            }
        };
       List<Users> list = userDao01.findAll(spec);
        System.out.println(list);
    }

方式二:

    /**
     * 多条件查询 方式二
     *
     * 需求,要求根据用户的姓名或者年龄查询数据
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test17(){
        Specification<Users> spec=new Specification<Users>() {
            @Override
            public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.or(criteriaBuilder.equal(root.get("userName"),"张三"),criteriaBuilder.equal(root.get("userAge"),27));
            }
        };
        List<Users> list = userDao01.findAll(spec);
        System.out.println(list);
    }

多条件查询的分页查询:

    /**
     * 需求:查询姓张的用户,并做分页处理
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test18(){
        //条件
        Specification<Users> spec=new Specification<Users>() {
            @Override
            public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

                return criteriaBuilder.like(root.get("userName").as(String.class),"张%");
            }
        };
        //分页
        Pageable pageable=PageRequest.of(0,2);
        Page<Users> page= userDao01.findAll(spec,pageable);
        System.out.println("总条数:"+page.getTotalElements());
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println(page.getContent());
    }

多条件查询的排序操作:

    /**
     * 需求:查询姓张的用户,并按用户年龄降序排序
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void  test19(){
        //条件
        Specification<Users> spec=new Specification<Users>() {
            @Override
            public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

                return criteriaBuilder.like(root.get("userName").as(String.class),"张%");
            }
        };
        //排序
        Sort sort=Sort.by(Sort.Direction.DESC,"userAge");
        List<Users> list=userDao01.findAll(spec,sort);
        System.out.println(list);
    }

多条件查询的分页加排序操作:

    /**
     * 需求:查询姓张的用户,做分页处理,然后按用户年龄降序排序
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void test20(){
        //排序
        Sort sort=Sort.by(Sort.Direction.DESC,"userAge");
        //分页
        Pageable pageable=PageRequest.of(0,2,sort);
        //查询条件
        Specification<Users> spec=new Specification<Users>() {
            @Override
            public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

                return criteriaBuilder.like(root.get("userName").as(String.class),"张%");
            }
        };
        Page<Users> page=userDao01.findAll(spec,pageable);
        System.out.println("总条数:"+page.getTotalElements());
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println(page.getContent());
    }

6.用户自定义Repository接口

我们在开发的时候,如果觉得他给的接口不足以满足需求的话,我们也可以自己去定义自己的Repository接口。

具体可以如下:

在repositoey包下面创建我们自己定义的Reposity接口,里面定义方法:

package com.haiexijun.repository;

import com.haiexijun.pojo.Users;

public interface UsersRepository {
    public Users findUserById(Integer userId);
}

然后我们定义自己的Dao类实现这个接口:

package com.haiexijun.dao;

import com.haiexijun.pojo.Users;
import com.haiexijun.repository.UsersRepository;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Repository
public class UserDaoImpl implements UsersRepository {
    @PersistenceContext(name = "entityManagerFactory")
    private EntityManager em;

    @Override
    public Users findUserById(Integer userId) {
        return this.em.find(Users.class,userId);
    }
}

我们也可以定义测试方法来调用一下这个dao:

    /**
     * 测试自定义Repository
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void test21(){
        Users user= userDao.findUserById(5);
        System.out.println(user);
    }

7.关联映射的操作

(1) 一对一的关联关系

案例需求:用户与角色的一对一的联级关系

用户一方,角色一方。

案例具体的步骤如下:

分别创建两个实体类Users实体类和Roles实体类:

Users


package com.haiexijun.pojo;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "t_users")
public class Users implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增
    @Column(name = "userid")
    private Integer userId;
    @Column(name = "username")
    private String userName;
    @Column(name = "userage")
    private Integer userAge;
     //dao具体要操作那个表,就对那个表的实体类添加CascadeType
    //通过cascade = CascadeType.PERSIST来进行级联操作,使Users表在更新的同时也能更新到Roles表
    @OneToOne(cascade = CascadeType.PERSIST)
    //JoinColumn的作用就是维护一个外键
    @JoinColumn(name = "roleid")
    private Roles roles;


    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getUserAge() {
        return userAge;
    }

    public void setUserAge(Integer userAge) {
        this.userAge = userAge;
    }

    public Roles getRoles() {
        return roles;
    }

    public void setRoles(Roles roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userAge=" + userAge +
                ", roles=" + roles +
                '}';
    }
}

Roles

package com.haiexijun.pojo;

import javax.persistence.*;

@Entity
@Table(name = "t_roles")
public class Roles {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "roleid")
    private Integer roleId;
    @Column(name = "rolename")
    private String roleName;
    @OneToOne(mappedBy = "roles")
    private Users users;

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public Users getUsers() {
        return users;
    }

    public void setUsers(Users users) {
        this.users = users;
    }

    @Override
    public String toString() {
        return "Roles{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                '}';
    }
}

之后会分别在两个实体类里面添加@OneToOne注解。在通过@JoinColumn(name = “roleid”)注解在任一个实体中定义好外键。

下面编写一个测试方法来具体演示如何操作:

    /**
     * 测试一对一的关联操作
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void test22(){
        //创建角色
        Roles roles=new Roles();
        roles.setRoleName("管理员");
        //创建用户
        Users users=new Users();
        users.setUserAge(30);
        users.setUserName("毛不易");
        //建立关系
        users.setRoles(roles);
        roles.setUsers(users);
        //保存数据
        usersDao.save(users);
    }

这样,我们就完成了根据一对一关联关系来操作了数据。

下面我们在来写一个方法来测试一下通过一对一关联关系的查询操作:

    @Test
    @Transactional
    @Rollback(value = false)
    public void test23(){
        Users users=usersDao.findByUserId(7);
        System.out.println("用户信息"+users);
        Roles roles=users.getRoles();
        System.out.println(roles);
    }

(2)一对多的关联关系

上一节,通过用户和角色来学习了一对一的关联关系。这一节我们还是通过用户与角色来学习一对多的关联关系。

需求:一个用户可以对应多个角色,但是一个角色可以对应多个用户。

这是从角色到用户的一对多的关系,或者说是从用户到角色的多对一的关联关系。

角色是一方,用户是多方。

先把之前学习一对一关联关系的Roles和Users实体拿来,然后把里面的@OneToOne等一对一的相关的注解给删掉。然后重新编写一对一的关联关系的相关的注解和配置。

Roles

package com.haiexijun.pojo;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "t_roles")
public class Roles {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "roleid")
    private Integer roleId;
    @Column(name = "rolename")
    private String roleName;

    @OneToMany(mappedBy = "roles")
    private Set<Users> users=new HashSet<>();


    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public Set<Users> getUsers() {
        return users;
    }

    public void setUsers(Set<Users> users) {
        this.users = users;
    }

}

Users

package com.haiexijun.pojo;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "t_users")
public class Users implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增
    @Column(name = "userid")
    private Integer userId;
    @Column(name = "username")
    private String userName;
    @Column(name = "userage")
    private Integer userAge;

    @ManyToOne(cascade =CascadeType.PERSIST)
    @JoinColumn(name = "roleid")
    private Roles roles;


    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getUserAge() {
        return userAge;
    }

    public void setUserAge(Integer userAge) {
        this.userAge = userAge;
    }

    public Roles getRoles() {
        return roles;
    }

    public void setRoles(Roles roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userAge=" + userAge +
                ", roles=" + roles +
                '}';
    }
}

上面注意了,要把一的一方的toString()方法给去掉,负责一对多的查询操作会报错。

下面编写测试方式来学习:

    /**
     * 测试一对多的关联操作
     *
     * 添加用户,同时添加角色
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void test24(){
        //创建角色
        Roles roles=new Roles();
        roles.setRoleName("普通用户");
        //创建用户
        Users users=new Users();
        users.setUserAge(50);
        users.setUserName("黄晓明");
        //建立关系
        roles.getUsers().add(users);
        users.setRoles(roles);
        //保存数据
        usersDao.save(users);

    }

}

下面再来编写代码学习一下一对多的关联查询:

    @Test
    @Transactional
    @Rollback(value = false)
    public void test25(){
        Users users= usersDao.findByUserName("黄晓明");
        System.out.println(users);
        Roles roles=users.getRoles();
        System.out.println(roles);
    }

(3)多对多的关联操作

需求:一个角色可以拥有多个菜单,一个菜单可以分配给多个角色。

角色是多方,菜也是多方。

我们需要用到之前的Roles的实体以及新创建一个Menus实体

Roles


package com.haiexijun.pojo;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "t_roles")
public class Roles {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "roleid")
    private Integer roleId;
    @Column(name = "rolename")
    private String roleName;

    @ManyToMany
    //JoinTable的作用:
    //它可以写在任一的多对多关系的实体中,配置中间表
    //joinColumns作用:建立当前表在中间表中的外键字段
    @JoinTable(name = "t_roles_menus",joinColumns = @JoinColumn(name = "role_id"),inverseJoinColumns = @JoinColumn(name ="menu_id"))
    private Set<Menus> menus=new HashSet<>();

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public Set<Menus> getMenus() {
        return menus;
    }

    public void setMenus(Set<Menus> menus) {
        this.menus = menus;
    }


}

Menus

package com.haiexijun.pojo;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "t_nemus")
public class Menus {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "menuid")
    private Integer menuId;
    @Column(name = "menuname")
    private String menuName;
    @Column(name = "namuurl")
    private String menuUrl;
    @Column(name = "menuid")
    private Integer fatherId;
    @ManyToMany(mappedBy ="menus" )
    private Set<Roles> roles=new HashSet<>();

    public Integer getMenuId() {
        return menuId;
    }

    public void setMenuId(Integer menuId) {
        this.menuId = menuId;
    }

    public String getMenuName() {
        return menuName;
    }

    public void setMenuName(String menuName) {
        this.menuName = menuName;
    }

    public String getMenuUrl() {
        return menuUrl;
    }

    public void setMenuUrl(String menuUrl) {
        this.menuUrl = menuUrl;
    }

    public Integer getFatherId() {
        return fatherId;
    }
  

    public void setFatherId(Integer fatherId) {
        this.fatherId = fatherId;
    }

    public Set<Roles> getRoles() {
        return roles;
    }

    public void setRoles(Set<Roles> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "Menus{" +
                "menuId=" + menuId +
                ", menuName='" + menuName + '\'' +
                ", menuUrl='" + menuUrl + '\'' +
                ", fatherId=" + fatherId +
                ", roles=" + roles +
                '}';
    }
}

下面编写测试代码来演示相关的操作:

    /**
     * 多对多关联操作
     *
     * 添加角色同时添加菜单
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void test26(){

        //创建角色对象
        Roles roles=new Roles();
        roles.setRoleName("超级管理员");
        //创建菜单对象
        //比如xxx管理平台,他下面会有用户管理等
        Menus menus1=new Menus();
        menus1.setMenuName("xxx管理平台");
        menus1.setFatherId(-1);
        menus1.setMenuUrl(null);

        Menus menus2=new Menus();
        menus2.setMenuName("用户管理");
        menus2.setFatherId(1);
        menus2.setMenuUrl(null);

        //建立关系
        roles.getMenus().add(menus1);
        roles.getMenus().add(menus2);

        menus1.getRoles().add(roles);
        menus2.getRoles().add(roles);

        //保存数据
        usersDao.save(roles);



    }

二.SpringBoot整合使用Spring Data Jpa

1.创建springboot的项目:

在这里插入图片描述

在这里插入图片描述

2.然后对项目的配置文件进行配置:

#数据库的配置
##指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建,如果指定create,则每次启动项目都会清空数据并删除表,再新建
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=mysql
#指定jpa的自动表生成策略,驼峰自动映射为下划线格式
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.datasource.url=jdbc:mysql://localhost:3306/jpa?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=zc20020106
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#是否显示sql在控制台
spring.jpa.show-sql=true

spring.jpa.hibernate.ddl-auto

create:
每次应用启动的时候会重新根据实体建立表,之前的表和数据都会被删除。
create-drop:
和上面的功能一样,但是多了一样,就是在应用关闭的时候,也就是sessionFactory一关闭,会把表删除。
update:
最常用的,第一次启动根据实体建立表结构,之后启动会根据实体的改变更新表结构,之前的数据都在。
validate:
会验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值,运行程序会校验实体字段与数据库已有的表的字段类型是否相同,不同会报错

然后创建一个pojo包,里面创建一个实体类,具体代码如下:

package com.example.springbootjpa.pojo;

import javax.persistence.*;

//@Entity注解标注实体类,必需
@Entity
//@Table注解:对应数据库中的表, 必须, name=表名, Indexes是声明表里的索引, columnList是索引的列, 同时声明此索引列是否唯一, 默认false
@Table(name="student",indexes = {@Index(name="id",columnList = "id",unique = true),@Index(name = "name",columnList = "name",unique = true)})
public class Student {
     @Id //id用于指明主键
     //@GeneratedValue: 表明是否自动生成, 必须, strategy也是必写, 指明主键生成策略, 默认是Oracle
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     // @Column: 对应数据库列名
     @Column(name = "id")
    private Long id;
    @Column(name = "name")
    private String name;
    @Column(name = "password")
    private String password;
    @Column(name = "email")
    private String email;
    @Column(name = "age")
    private Integer age;

    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 String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                '}';
    }
}

3 然后编写一个StudentDao并继承自JpaRepository,由此我们已经继承了大部分可用的CRUD操作,针对基础操作,DAO完全不用写任何方法。

package com.example.springbootjpa.dao;

import com.example.springbootjpa.pojo.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentDao extends JpaRepository<Student,Long> {
  
}

4.接着编写一个服务接口,添加学生的保存、删除、查询全部和分页查询等的方法。

package com.example.springbootjpa.service;

import com.example.springbootjpa.pojo.Student;
import org.springframework.data.domain.Pageable;

public interface StudentService {

    //保存学生
    public void save(Student student);

    //删除学生
    public void delete(Student student);

    //查询分页数据
    public Object findPage(Pageable pageable);


}

5.继续编写服务实现类并调用DAO实现相应功能

package com.example.springbootjpa.service.impl;

import com.example.springbootjpa.dao.StudentDao;
import com.example.springbootjpa.pojo.Student;
import com.example.springbootjpa.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentDao studentDao;


    @Override
    public void save(Student student) {
        studentDao.save(student);
    }

    @Override
    public void delete(Student student) {
        studentDao.delete(student);
    }

    @Override
    public Object findPage(Integer PageNum,Integer pageSize) {
        Pageable pageable= PageRequest.of(PageNum,pageSize);
        return studentDao.findAll(pageable);
    }
}

6.接着编写一个学生控制器,调用服务接口实现对应功能。

package com.example.springbootjpa.controller;

import com.example.springbootjpa.pojo.Student;
import com.example.springbootjpa.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/student")
public class StudentController {
    @Autowired
    private StudentService studentService;

    @PostMapping("/save")
    public void save(@RequestBody Student student){
        studentService.save(student);
    }

    @PostMapping("/delete")
    public void delete(@RequestBody Student student){
        studentService.delete(student);
    }

    @GetMapping("/findAll")
    public Object findAll(){
        return studentService.findAll();
    }

    @GetMapping("/findPage")
    public Object findPage(@RequestParam Integer pageNum,@RequestParam Integer pageSize){
        return studentService.findPage(pageNum,pageSize);
    }

}

到这里基本的整合与使用都介绍完了。如果以后我遇到其他的用法和知识点我再回来添加。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要在Spring Boot中整合Spring Data JPA,可以按照以下步骤进行操作: 1. 添加依赖:在项目的pom.xml文件中添加Spring Data JPA的依赖。可以使用以下依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ``` 2. 配置数据源:在Spring的配置文件中配置数据源,例如application.properties或application.yml文件。根据你使用的数据库类型,配置对应的数据源相关信息,如数据库连接URL、用户名、密码等。 3. 定义实体类:创建与数据库表对应的实体类,并使用JPA注解进行映射。例如,在实体类上使用@Entity注解,指定表名、字段名,以及关系映射等。 4. 创建DAO接口:创建一个继承自JpaRepository或其子接口的DAO接口。这个接口将提供一些常用的CRUD操作方法,无需手动实现。 5. 编写业务逻辑:在Service层中编写业务逻辑代码,并调用DAO接口中定义的方法进行数据访问和操作。 6. 运行程序:启动Spring Boot应用程序,Spring Boot会自动创建数据源、实体类和DAO接口的实现,并根据需要执行数据库操作。 以下是一个简单的示例: 1. 创建实体类: ```java @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // getters and setters } ``` 2. 创建DAO接口: ```java public interface UserRepository extends JpaRepository<User, Long> { // 可以在此接口中添加自定义的查询方法 } ``` 3. 编写业务逻辑: ```java @Service public class UserService { @Autowired private UserRepository userRepository; public List<User> getAllUsers() { return userRepository.findAll(); } public User getUserById(Long id) { return userRepository.findById(id).orElse(null); } public User saveUser(User user) { return userRepository.save(user); } public void deleteUser(Long id) { userRepository.deleteById(id); } } ``` 通过以上步骤,你就可以在Spring Boot应用程序中使用Spring Data JPA进行数据库访问和操作了。可以通过注入UserService来调用相应的方法,实现对数据库的增删改查操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

害恶细君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值