美文网首页开发js css html
Spring Data JPA-基础篇(二)

Spring Data JPA-基础篇(二)

作者: 石头耳东 | 来源:发表于2022-09-10 16:54 被阅读0次

    前置文章:

    Spring Data JPA-基础篇(一)

    前言:

    前置文章中我们已经介绍了基础JPA的使用方式,JPA是操作数据库的一种ORM规范,而文章中使用的Hibernate是其具体的实现。
    本文则是使用Spring Data JPA来进行具体的CRUD操作。

    零、本文纲要

    一、基础准备

    1. pom.xml配置
    2. applicationContext.xml
    3. Customer实体类
    4. CustomerDao接口

    二、基础CRUD

    1. 测试准备
    2. 测试查询
    3. 测试保存
    4. 测试删除
    5. 测试查询所有
    6. 测试聚合查询
    7. 判断是否存在

    三、JPQL形式查询

    1. 条件查询
    2. 多条件查询
    3. 更新操作

    四、SQL形式查询

    1. 查询全部
    2. 模糊匹配

    五、方法命名规则查询

    1. 条件查询
    2. 模糊匹配
    3. 多条件查询

    一、基础准备

    1. pom.xml配置

    ① 属性控制
        <!--属性控制-->
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <spring.version>5.1.20.RELEASE</spring.version>
            <hibernate.version>5.0.7.Final</hibernate.version>
            <javax.el.version>2.2.4</javax.el.version>
            <slf4j.version>1.6.6</slf4j.version>
        </properties>
    
    ② 依赖管理

    此处依赖部分并没有过多说明,因为现在大家使用的Spring Boot、Spring Cloud都在淡化我们依赖配置的内容,更关注代码开发本身。

        <!--依赖管理-->
        <dependencies>
            <!--Junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
            <!--Spring-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.6.0</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-jpa</artifactId>
                <version>1.9.0.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <!--el-->
            <dependency>
                <groupId>javax.el</groupId>
                <artifactId>javax.el-api</artifactId>
                <version>${javax.el.version}</version>
            </dependency>
            <dependency>
                <groupId>org.glassfish.web</groupId>
                <artifactId>javax.el</artifactId>
                <version>${javax.el.version}</version>
            </dependency>
            <!--hibernate-->
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-core</artifactId>
                <version>${hibernate.version}</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-entitymanager</artifactId>
                <version>${hibernate.version}</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>5.2.1.Final</version>
            </dependency>
            <!--MySQL-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.30</version>
            </dependency>
            <!--c3p0数据库连接池-->
            <dependency>
                <groupId>c3p0</groupId>
                <artifactId>c3p0</artifactId>
                <version>0.9.1.2</version>
            </dependency>
            <!--log-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
        </dependencies>
    

    2. applicationContext.xml

    ① 基础约束准备

    引入了beans、aop、context、jdbc、tx、jpa这些命名空间。

    <?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:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:jdbc="http://www.springframework.org/schema/jdbc"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:jpa="http://www.springframework.org/schema/data/jpa"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/jdbc
           http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/data/jpa
           http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    
    </beans>
    
    ② 数据源配置
        <!-- 1. 创建数据库连接池 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
            <property name="jdbcUrl" value="jdbc:mysql:///test"/>
            <property name="user" value="root"/>
            <property name="password" value="root"/>
        </bean>
    
    ③ 创建entityManagerFactory对象
        <!-- 2. 创建entityManagerFactory对象 -->
        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <!-- ① 配置数据源 -->
            <property name="dataSource" ref="dataSource"/>
    
            <!-- ② 指定实体类所在的包 -->
            <property name="packagesToScan" value="com.stone.domain"/>
    
            <!-- ③ 配置JPA的实现厂商(供应商) -->
            <property name="persistenceProvider">
                <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
            </property>
    
            <!-- ④ JPA的供应商适配器 -->
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                    <!-- a、配置是否自动创建数据库表 -->
                    <property name="generateDdl" value="false"/>
                    <!-- b、指定数据库类型 -->
                    <property name="database" value="MYSQL"/>
                    <!-- c、数据库方言:支持的特有语法,MySQL分页limit,Oracle分页ROWNUM -->
                    <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
                    <!-- d、是否显示SQL语句 -->
                    <property name="showSql" value="true"/>
                </bean>
            </property>
    
            <!--⑤ JPA方言:高级特性-->
            <property name="jpaDialect">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
            </property>
        </bean>
    
    ④ 配置事务管理器
        <!-- 3. 配置事务管理器 -->
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"/>
        </bean>
    
    ⑤ 整合Spring Data JPA
        <!-- 4. 整合Spring Data JPA -->
        <jpa:repositories base-package="com.stone.dao" transaction-manager-ref="transactionManager"
                          entity-manager-factory-ref="entityManagerFactory"/>
    
    ⑥ 声明式事务

    此处非必要

        <!-- txAdvice -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="save" propagation="REQUIRED"/>
                <tx:method name="insert" propagation="REQUIRED"/>
                <tx:method name="update" propagation="REQUIRED"/>
                <tx:method name="delete" propagation="REQUIRED"/>
                <tx:method name="get*" read-only="true"/>
                <tx:method name="find*" read-only="true"/>
                <tx:method name="*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
        <!-- aop -->
        <aop:config>
            <aop:pointcut id="pointcut" expression="execution(* com.stone.service.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
        </aop:config>
    
    ⑦ 配置包扫描
        <context:component-scan base-package="com.stone"/>
    
    ⑧ 整合其他配置文件

    此处暂时没有。

    3. Customer实体类

    /**
     * 客户实体类
     * → 配置映射关系
     *      1. 实体类和表的映射关系;
     *      2. 实体类中属性和表中字段的映射关系;
     * → 实体类和表的映射关系
     *      ① @Entity:声明实体类;
     *      ② @Table:配置实体类和表的映射关系;
     *          → name:配置数据库表名称;
     * → 实体类中属性和表中字段的映射关系
     *      ① @Id:声明主键的配置;
     *      ② @GeneratedValue:配置主键生成策略;
     *          → strategy:主键策略
     *              a、 GenerationType.IDENTITY  自增,MySQL数据库;
     *                  底层数据库必须支持自动增长,采用数据库自增方式对ID进行自增。
     *              b、 GenerationType.SEQUENCE  序列,Oracle数据库;
     *                  底层数据库支持序列。
     *              c、 GenerationType.TABLE
     *                  JPA提供的一种机制,通过一张数据库表的形式帮助完成主键自增。
     *              d、 GenerationType.AUTO
     *                  由程序自动选择主键生成策略。
     *      ③ Column:实体类属性与表字段映射
     *          → name:数据库表字段名称
     */
    @Table(name = "cst_customer")
    @Entity
    public class Customer {
    
        /*客户主键*/
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "cust_id")
        private Long custId;
    
        /*客户名称*/
        @Column(name = "cust_name")
        private String custName;
    
        /*客户资源来源*/
        @Column(name = "cust_source")
        private String custSource;
    
        /*客户级别*/
        @Column(name = "cust_level")
        private String custLevel;
    
        /*客户所属行业*/
        @Column(name = "cust_industry")
        private String custIndustry;
    
        /*客户的联系方式*/
        @Column(name = "cust_phonoe")
        private String custPhone;
    
        /*客户地址*/
        @Column(name = "cust_address")
        private String custAddress;
    
        ...
    
    }
    

    4. CustomerDao接口

    NOTE
    JpaRepository<T, ID>,该接口封装了基础CRUD操作:
    → T:操作的实体类类型;
    → ID:实体类中主键属性的类型。

    JpaSpecificationExecutor<T>,该接口封装了复杂查询(分页):
    → T:操作的实体类类型。

    /**
     * 符合SpringDataJpa规范的接口
     * → JpaRepository<T, ID>,该接口封装了基础CRUD操作
     * T:操作的实体类类型;
     * ID:实体类中主键属性的类型。
     * → JpaSpecificationExecutor<T>,该接口封装了复杂查询(分页)
     * T:操作的实体类类型。
     */
    public interface CustomerDao extends JpaRepository<Customer, Long>, 
    JpaSpecificationExecutor<Customer> {
        ...
    }
    

    二、基础CRUD

    1. 测试准备

    @RunWith(SpringJUnit4ClassRunner.class) //声明Spring提供的单元测试环境
    @ContextConfiguration(locations = "classpath:applicationContext.xml") //指定Spring容器配置信息
    public class CustomerDaoTest {
    
        @Autowired
        private CustomerDao customerDao;
    
        ...
    
    }
    

    2. 测试查询

    NOTE
    findOne → em.find() → 立即加载;
    getOne → em.getReference() → 延迟加载;

        /**
         * 测试跟据ID查询,findOne方法
         */
        @Test
        public void testFindOne() {
            Customer customer = customerDao.findOne(1L);
            System.out.println(customer);
        }
    
        /**
         * 跟据ID从数据库查询,getOne方法需要使用@Transactional注解
         * findOne → em.find():立即加载
         * getOne → em.getReference():延迟加载
         */
        @Test
        @Transactional
        public void testGetOne() {
            Customer customer = customerDao.getOne(5L);
            System.out.println(customer);
        }
    

    3. 测试保存

    NOTE
    ① 实体对象未设置主键属性值:直接保存;
    ② 实体对象设置主键属性值:
    → a、主键存在,进行更新;
    → b、主键不存在,跟据主键策略生成主键进行插入(保存)。

        /**
         * 测试更新|保存,save方法
         * → 先查询,后更新|保存
         * 如果没有设置ID属性,则进行保存操作;如果设置了ID属性,则会进行更新|保存。
         * ① 如果指定ID数据存在,则进行更新操作;
         * ② 如果指定ID数据不存在,则进行保存操作(注意:保存时主键会依据主键策略自动生成,而不是指定的主键)。
         */
        @Test
        public void testSave() {
            Customer customer = new Customer();
            customer.setCustId(22L);
            customer.setCustName("Stone");
            customerDao.save(customer);
        }
    

    4. 测试删除

        /**
         * 测试删除delete
         * → 先查询,后删除
         */
        @Test
        public void testDelete() {
            customerDao.delete(21L);
        }
    

    5. 测试查询所有

        /**
         * 测试查询所有findAll
         */
        @Test
        public void testFindAll() {
            List<Customer> customers = customerDao.findAll();
            for (Customer customer : customers) {
                System.out.println(customer);
            }
        }
    

    6. 测试聚合查询

        /**
         * 测试聚合(统计)查询
         */
        @Test
        public void testCount() {
            long count = customerDao.count();
            System.out.println(count);
        }
    

    7. 判断是否存在

        /**
         * 测试查询判断是否存在
         * 传统方式:
         * ① 判断查询结果是否为空null;
         * ② 判断查询结果条数是否大于0;【JPA采用此方式】
         */
        @Test
        public void testExists() {
            boolean exists = customerDao.exists(4L);
            System.out.println(exists);
        }
    

    三、JPQL形式查询

    1. 条件查询

    ① 接口方法准备
        /**
         * 跟据客户名称查询客户信息
         * JPQL:FROM Customer WHERE custName = ?
         * 注意:在@Query注解的value属性中,需要使用"?1"来指定具体占位。
         *
         * @param custName 客户名称
         * @return 客户信息
         */
        @Query(value = "FROM Customer WHERE custName = ?1")
        public Customer findJpql(String custName);
    
    ② 测试
        /**
         * 测试JPQL查询
         */
        @Test
        public void testJpql() {
            Customer yinRui = customerDao.findJpql("Yin Rui");
            System.out.println(yinRui);
        }
    

    2. 多条件查询

    ① 接口方法准备
        /**
         * JPQL:FROM Customer WHERE custName = ? AND custId = ?
         *
         * @param custName 客户名称
         * @param custId   客户ID
         * @return 客户信息
         */
        @Query(value = "FROM Customer WHERE custName = ?1 AND custId = ?2")
        public Customer findCustNameAndCustId(String custName, Long custId);
    
    ② 测试
        /**
         * 测试JPQL多条件查询
         */
        @Test
        public void testJpqlAnd() {
            Customer nicholasDunn = customerDao.findCustNameAndCustId("Nicholas Dunn", 19L);
            System.out.println(nicholasDunn);
        }
    

    3. 更新操作

    ① 接口方法准备

    NOTE
    相比于查询方法,此处需要配合 @Modifying 注解一起使用。

        /**
         * 更新客户姓名(@Query:代表进行查询;@Modifying:代表当前执行方法进行更新操作。)
         * SQL:UPDATE cst_customer SET cust_name = ? WHERE cust_id = ?
         * JPQL:UPDATE Customer SET custName = ? WHERE custId = ?
         *
         * @param custName
         * @param custId
         */
        @Query(value = "UPDATE Customer SET custName = ?1 WHERE custId = ?2")
        @Modifying //(@Modifying+@Query)一起使用,代表更新操作。
        public void updateCustomer(String custName, Long custId);
    
    ② 测试
        /**
         * 测试JPQL更新操作
         * → SpringDataJpa中使用jpql完成 更新|删除操作 需要手动添加事务支持;
         * → 在@Test注解下,默认SpringDataJpa执行完成会进行数据回滚;
         * → 使用@Rollback(value = false),关闭自动回滚。
         */
        @Test
        @Transactional //添加事务支持
        @Rollback(value = false)
        public void testUpdate() {
            customerDao.updateCustomer("Jeff Stone", 1L);
        }
    

    四、SQL形式查询

    1. 查询全部

    ① 接口方法准备
        /**
         * SQL:SELECT * FROM cst_customer
         * → 使用@Query注解,配置:
         * ① value属性:sql;
         * ② nativeQuery属性:使用本地sql查询,true;反之,false。
         *
         * @return 查询对象集合
         */
        @Query(value = "SELECT * FROM cst_customer", nativeQuery = true)
        public List<Object[]> findSql();
    
    ② 测试
        /**
         * 测试使用本地SQL查询全部客户信息
         */
        @Test
        public void testSql() {
            List<Object[]> objects = customerDao.findSql();
            for (Object[] object : objects) {
                System.out.println(Arrays.toString(object));
            }
        }
    

    2. 模糊匹配

    ① 接口方法准备
        /**
         * 使用SQL查询,进行模糊匹配
         * SQL:SELECT * FROM cst_customer WHERE cust_name LIKE ?
         *
         * @param custName 客户名称
         * @return 客户信息
         */
        @Query(value = "SELECT * FROM cst_customer WHERE cust_name LIKE ?1", nativeQuery = true)
        public List<Object[]> findSqlLike(String custName);
    
    ② 测试
        /**
         * 测试SQL查询,模糊匹配
         */
        @Test
        public void testSqlLike() {
            List<Object[]> objects = customerDao.findSqlLike("%Hicks");
            for (Object[] object : objects) {
                System.out.println(Arrays.toString(object));
            }
        }
    

    五、方法命名规则查询

    1. 条件查询

    ① 接口方法准备

    NOTE
    查询方法写法:findBy + 属性名称(field_name),等值匹配

        /**
         * 方法命名规则查询,是对JPQL查询的更深层的封装,使用该规则可以不用再写JPQL语句。
         * → 查询方法写法:findBy + 属性名称(field_name),等值匹配
         * ① 以findBy开头;
         * ② 拼接对象中的属性,属性首字母改为大写;
         *
         * @param custName 客户名称
         * @return 客户信息
         */
        public Customer findByCustName(String custName);
    
    ② 测试
        /**
         * 测试按照方法命名规则查询
         */
        @Test
        public void testFindByCustName() {
            Customer itoRiku = customerDao.findByCustName("Ito Riku");
            System.out.println(itoRiku);
        }
    

    2. 模糊匹配

    ① 接口方法准备

    NOTE
    查询方法写法:findBy + 属性名称(field_name) + 查询方式[Like|Isnull]

        /**
         * 跟据方法命名规则,进行模糊匹配
         * → 查询方法写法:findBy + 属性名称(field_name) + 查询方式[Like|Isnull]
         *
         * @param custName 客户名称
         * @return 客户信息
         */
        public List<Customer> findByCustNameLike(String custName);
    
    ② 测试
        /**
         * 测试模糊匹配查询(注意:由于时占位符的形式进行填充,"%"的位置信息需要自己判断。)
         */
        @Test
        public void testFindByCustNameLike() {
            List<Customer> customers = customerDao.findByCustNameLike("%Yuito");
            for (Customer customer : customers) {
                System.out.println(customer);
            }
        }
    

    3. 多条件查询

    ① 接口方法准备

    NOTE
    查询方法写法:findBy + 属性名称(field_name) + 查询方式[Like|Isnull] + 多条件连接符(And|Or) + 属性名称(field_name) + 查询方式[Like|Isnull]

        /**
         * 跟据方法命名规则,进行多条件查询
         * → 查询方法写法:findBy + 属性名称(field_name) + 查询方式[Like|Isnull] + 多条件连接符(And|Or)
         * + 属性名称(field_name) + 查询方式[Like|Isnull]
         *
         * @param CustName 客户名称
         * @param CustAddress 客户地址
         * @return 客户信息
         */
        public List<Customer> findByCustNameLikeAndCustAddress(String CustName, String CustAddress);
    
    ② 测试
        /**
         * 测试多条件查询
         */
        @Test
        public void testFindByCustNameLikeAndCustAddress() {
            List<Customer> customers =
                    customerDao.findByCustNameLikeAndCustAddress("Xue%", "536 Wall Street");
            for (Customer customer : customers) {
                System.out.println(customer);
            }
        }
    

    六、结尾

    以上即为Spring Data JPA-基础篇(二)的全部内容,感谢阅读。

    相关文章

      网友评论

        本文标题:Spring Data JPA-基础篇(二)

        本文链接:https://www.haomeiwen.com/subject/vhltortx.html