zoukankan      html  css  js  c++  java
  • SpringDataJpa学习(2)——SpringDataJpa的单表使用

    写在前面

    本文上接SpringDataJpa学习(1)——Jpa学习,在学习了Jpa规范后,来学习一下SpringDataJpa的使用。SpringDataJpa是spring公司推出的对jpa规范的深层封装。SpringDataJpa极大简化了数据库访问层代码,可以让我们免除各种简单的sql语句。

    使用SpringDataJpa的环境准备

    导入坐标

    <properties>
            <spring.version>5.2.3.RELEASE</spring.version>
            <hibernate.version>5.0.7.Final</hibernate.version>
            <slf4j.version>1.6.6</slf4j.version>
            <log4j.version>1.2.12</log4j.version>
            <c3p0.version>0.9.1.2</c3p0.version>
            <mysql.version>5.1.6</mysql.version>
        </properties>
    
        <dependencies>
            <!-- junit单元测试 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
    
            <!-- spring begin -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.6.8</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</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>
            <!-- spring对orm框架的支持包 -->
            <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-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
    
            <!-- spring end -->
    
            <!-- hibernate begin -->
            <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>
            <!-- hibernate end -->
    
            <!-- c3p0 begin -->
            <dependency>
                <groupId>c3p0</groupId>
                <artifactId>c3p0</artifactId>
                <version>${c3p0.version}</version>
            </dependency>
            <!-- c3p0 end -->
    
            <!-- log begin -->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</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>
            <!-- log end -->
    
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!-- -->
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-jpa</artifactId>
                <version>2.3.1.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${spring.version}</version>
            </dependency>
    
            <!-- el begin 使用spring data jpa 必须引入 -->
            <dependency>
                <groupId>javax.el</groupId>
                <artifactId>javax.el-api</artifactId>
                <version>3.0.1-b06</version>
            </dependency>
    
            <dependency>
                <groupId>org.glassfish.web</groupId>
                <artifactId>javax.el</artifactId>
                <version>2.2.6</version>
            </dependency>
            <!-- el end -->
        </dependencies>
    

    书写配置文件

    由于是spring的一套规范,我们肯定要写spring的配置文件,如下。

    <?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" xmlns:task="http://www.springframework.org/schema/task"
           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">
        <!-- spring 和 spring data jpa的配置 -->
        <!-- 1.创建entityManagerFactory对象交给spring容器管理-->
        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <!-- 配置的扫描的包(实体类所在的包) -->
            <property name="packagesToScan" value="com.liuge.domain" />
            <!-- jpa的实现方式-->
            <property name="persistenceProvider">
                <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
            </property>
            <!-- jpa的供应商适配器 -->
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                    <!-- 配置自动创建数据库表 -->
                    <property name="generateDdl" value="false" />
                    <!-- 指定数据库类型 -->
                    <property name="database" value="MYSQL" />
                    <!-- 数据库方言:支持的特有语法 -->
                    <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
                    <!-- 是否显示sql -->
                    <property name="showSql" value="true" />
                </bean>
            </property>
            <!-- jpa的方言: 高级的特性-->
            <property name="jpaDialect">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
            </property>
         </bean>
    
        <!-- 2. 创建一个数据库连接池-->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="user" value="root"/>
            <property name="password" value="abc456"/>
            <property name="jdbcUrl" value="jdbc:mysql:///jpatest?serverTimezone=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;allowMultiQueries=true"/>
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        </bean>
    
        <!-- 3.整合SpringDataJpa -->
        <jpa:repositories base-package="com.liuge.dao" transaction-manager-ref="transactionManager"
                          entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
        <!-- 4.配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"/>
        </bean>
        <!-- 5.声明式事务-->
        <!-- 6.配置包扫描注解-->
        <context:component-scan base-package="com.liuge"/>
    </beans>
    

    配置映射关系

    还是和之前一样,在实体类上配置:

    @Entity
    @Table(name = "cst_customer")
    public class Customer {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "cust_id")
        private Long custId;
        @Column(name = "cust_address")
        private String custAddress;
        @Column(name = "cust_industry")
        private String custIndustry;
        @Column(name = "cust_level")
        private String custLevel;
        @Column(name = "cust_name")
        private String custName;
        @Column(name = "cust_phone")
        private String custPhone;
        @Column(name = "cust_source")
        private String custSource;
    }
    

    并且生成对应的get和set方法以及toString即可。

    编写符合SpringDataJpa规范的Dao层接口

    /**
     * JpaRepository<操作的实体类类型,实体类中主键属性的类型>
     * 封装了基本CRUD操作
     * JpaSpecificationExecutor<操作的实体类类型>
     * 封装了复杂查询(分页)
     *
     * @author wushen
     */
    public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {}
    

    实际上,我们只需要定义一个接口然后继承规定好的接口并写好泛型即可。

    使用SpringDataJpa

    在配置好后,我们可以写一个测试类来测试一下:

    测试根据id查询

    @RunWith(SpringJUnit4ClassRunner.class) // 声明单元测试环境
    @ContextConfiguration(locations = "classpath:applicationContext.xml") //指定spring容器的配置信息
    public class CustomerDaoTest {
        @Autowired
        private CustomerDao customerDao;
    
        /**
         * 根据id查询
         */
        @Test
        public void testFindOne() {
            Optional<Customer> one = customerDao.findById(3L);
            if (one.isPresent()) {
                System.out.println(one.get());
            }
        }
    }
    

    由于是Spring的相关组件,我们要在测试类上配置好spring的相关测试信息,并且设置自动注入。
    这里我们测试了一个简单的findById方法,会返回一个封装类,这都是SpringDataJpa规定好的,我们通过isPresent()方法判空,然后get()方法取得该对象即可。

    测试添加和修改

      /**
         * save:保存或者更新
         * 根据传递的对象是否存在主键id,如果没有id主键属性:保存
         * 存在id主键属性,根据id查询数据库,更新数据
         */
        @Test
        public void testSave() {
            Customer customer = new Customer();
            customer.setCustName("我的天呐");
            customer.setCustLevel("VIP");
            customer.setCustIndustry("吔屎国");
            customerDao.save(customer);
        }
    
        /**
         * 更新
         */
        @Test
        public void testUpdate() {
            Customer customer = new Customer();
            customer.setCustId(4L);
            customer.setCustName("我的天呐");
            customer.setCustLevel("VIP");
            customer.setCustIndustry("吔屎国");
            Date date = new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String dateTime = simpleDateFormat.format(date);
            customer.setCustName(dateTime);
            customerDao.save(customer);
        }
    

    在接口定义中,save既是保存也是更新,这主要取决于是否包含id。

    延迟加载的根据id查询

        /**
         * 根据id从数据库查询
         *
         * @Transactional 保证getOne正常运行
         * <p>
         * findById:em.find()
         * getOne:em.getReference() 延迟加载
         * 返回的是一个客户的动态代理对象,什么时候用,什么时候查询
         */
        @Test
        @Transactional
        public void testGetOne() {
            Customer customer = customerDao.getOne(4L);
            System.out.println(customer);
        }
    

    与JPA相似,这里也有延迟加载。

    使用jpql的方式查询

    既然是封装的jpa规范,肯定也能使用jpql。我们首先在dao接口定义方法:

        /**
         * 根据客户名称查询客户
         * 使用jpql的形式查询
         * jpql: from Customer where custName = ?
         * 配置jpql语句,使用@Query注解
         * 也可以是这种形式
         */
        @Query(value = "from Customer where custName = :custName")
        Customer findJpql(@Param("custName") String custName);
    

    之后使用即可:

        public void testFindJPQL(){
            Customer customer = customerDao.findJpql("我去");
            System.out.println(customer);
        }
    
    

    这里我们注意到,query语句里使用了这样的形式。其实我们也可以使用下面这种形式:

        /**
         * 根据客户名称和客户id查询客户
         * jpql:from Customer where custName = ? and custId = ?
         * 可以是这种形式
         *
         * @param custName
         * @param custId
         * @return
         */
        @Query(value = "from Customer where custName = ?1 and custId = ?2")
        Customer findCustNameAndId(String custName, Long custId);
    

    一种是通过索引,一种是通过标注@param后直接在语句中写上:加名字即可

    测试更新或删除

    更新与删除有点点不同,需要添加一个@Modifying注解:

        /**
         * 使用jpql完成更新操作
         * 根据id更新客户的名称
         * sql: update cst_customer set cust_name = ? where cust_id = ?
         * jpql:update Customer set custName = ? where custId = ?
         * 需要手动添加事务的支持
         *
         * @modifying 注解以通知这是一个delete或update操作
         */
        @Query(value = "update Customer set custName = ?2 where custId = ?1")
        @Modifying
        void updateCustomer(Long custId, String custName);
    

    编写测试类:

        @Test
        @Transactional
        public void testUpdate(){
            customerDao.updateCustomer(2L,"我去");
        }
    

    需要加上支持事务的注解。

    使用原生sql查询

    SpringDataJpa也是支持原生sql查询的,我们在dao中定义方法:

        /**
         * 以sql的形式查询
         * 查询全部的客户
         * sql: select * from cst_customer;
         * nativeQuery:true:sql查询
         * false:jpql查询
         */
        @Query(value = "select * from cst_customer", nativeQuery = true)
        List<Object[]> findSql();
    

    只要把nativeQuery改成true即可使用原生sql(默认是false,使用jpql)
    编写测试类:

        /**
         * 测试sql查询
         */
        @Test
        public void testFindSql(){
            List<Object[]> list = customerDao.findSql();
            for (Object[] objects : list) {
                System.out.println(Arrays.toString(objects));
            }
        }
    

    类似的,我们也可以使用占位符(?)

        /**
         * 带参数的模糊查询sql
         * @param custName
         * @return
         */
        @Query(value = "select * from cst_customer where cust_name like ?1", nativeQuery = true)
        List<Object[]> findSqlWithParam(String custName);
    

    编写测试类:

        /**
         * 测试带参数的模糊sql查询
         */
        @Test
        public void testFindSqWithParam(){
            List<Object[]> list = customerDao.findSqlWithParam("我%");
            for (Object[] objects : list) {
                System.out.println(Arrays.toString(objects));
            }
        }
    

    按照spring官方定义的命名规则的查询

    实际上,我们不必自己写jpql语句,只需要按照官方的命名方式命名即可。我上官方文档看了看,目前有这些:

    可以看到这些基本包含了所有的查询方法了。我们自己写一个试试:

        /**
         * 方法名的约定:
         * findBy:查询
         *          对象中的属性名(首字母大写) :查询的条件
         *          findByCustName -- 根据客户名称查询  默认情况:使用等于的方式查询
         *
         *   在springdatajpa的运行阶段会根据方法名称进行解析, findBy from xx(实体类)
         *                                                     属性名 where custName =
         * @param custName
         * @return
         */
        Customer findByCustName(String custName);
    

    编写测试类:

        /**
         * 测试方法命名规则的查询
         */
        @Test
        public void testFindByCustName(){
            Customer customer = customerDao.findByCustName("我去");
            System.out.println(customer);
        }
    

    其他的还有很多,这里就不再一一测试了,按照表格对应的关系书写即可。

    总结

    可以看到SpringDataJpa的功能十分强大,几乎涵盖了全部的常用应有场景。但这里并没有涉及到多表的配置。之后会学习如何配置多表。

  • 相关阅读:
    领导力包括哪些能力?如何提升领导力?
    管理者如何让员工服从?
    常用查询语句
    BZOJ2190 SDOI2008 仪仗队 gcd,欧拉函数
    使用python来操作redis用法详解
    int指令
    浏览器打开exe文件
    feiQ发送信息
    【转】Notepad++ 中文查找(中文搜索)问题解决方法
    【转】Java基本概念:集合类 List/Set/Map...的区别
  • 原文地址:https://www.cnblogs.com/wushenjiang/p/13196903.html
Copyright © 2011-2022 走看看