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的功能十分强大,几乎涵盖了全部的常用应有场景。但这里并没有涉及到多表的配置。之后会学习如何配置多表。

猜你喜欢

转载自www.cnblogs.com/wushenjiang/p/13196903.html