04.spring framework的DAO支持

一、Spring数据访问原则

1、将数据访问的功能放到一个或多个专注于此项任务的组件中。这样的组件通常称为数据访问对象(data access objectDAO)或Repository

2、编写良好的Repository应该以接口的方式暴露数据访问层所提供的功能(好处1:便于测试,好处2:数据访问的具体实现通过接口进行了隔离);

3、为了将数据访问层与应用程序的其他部分隔离开来,

二、Spring数据访问异常体系

1、目的

将数据访问层与应用程序的其他部分隔离开来,Spring采用的方式之一就是提供统一的数据访问异常体系

2、spring异常体系优势

Spring数据访问的异常体系用来解决:

  • JDBC的异常体系过于简单——实际上,JDBC的异常体系算不上一个体系。
  • Hibernate的异常体系是其本身所独有的。我们需要的数据访问异常要具有描述性而且又与特定的持久化框架无关。
不管对于JPA还是Hibernate,异常转换都不是强制要求的。如果你希望在Repository中抛出特定的JPAHibernate异常,只需
PersistenceException-TranslationPostProcessor省略掉即可,这样原来的异常就会正常地处理。但是,如果使用了Spring
的异常转换,你会将所有的数据访问异常置于Spring的体系之下,这样以后切换持久化机制的话会更容易。

3、特点

  • 没有与特定的持久化方式相关联
  • 异常种类很多、很细粒度
  • 异常都继承自DataAccessException。这是非检查型异常:可以根据需要选择是否捕获。之所以spring这么设计是因为Spring认为触发异常的很多问题是不能(无法)在catch代码块中修复的,比如数据库连接失败等等,是应用程序无法解决的。而不是强制开发人员编写catch代码块(里面经常是空的)。这把是否要捕获异常的权力留给了开发人员。

4、使用

为了利用Spring的数据访问异常,我们必须使用Spring所支持的数据访问模板

三、数据访问模板化

1、思想

    尽管在某个复杂的过程中包含多个步骤,但是涉及到子类实现参与的只有几个。承运人(抽象层)负责推动整个流程。你只会在必要的时候进行参与,其余的过程不必关心。这反映了一个强大的设计模式:模板方法模式
    这也是Spring在数据访问中所使用的模式。不管我们使用什么样的技术,都需要一些特定的数据访问步骤。例如,我们都需要获取一个到数据存储的连接并在处理完成后释放资源。这都是在数据访问处理过程中的固定步骤,但是每种数据访问方法又会有些不同,我们会查询不同的对象或以不同的方式更新数据,这都是数据访问过程中变化的部分
 
2、设计
 
Spring将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板(template)回调(callback)
 
这两部分职责与协作:
 
  • Spring的模板类负责:事务控制、 管理资源、处理异常(处理数据访问的固定部分)。
  • 回调负责:应用程序相关的数据访问的语句、 绑定参数、整理结果集

3、使用(实现)

针对不同的持久化平台,Spring提供了多个可选的模板

​​​​下表是Spring提供的数据访问模板,分别适用于不同的持久化机制:

 
jca.cci.core.CciTemplate
JCA CCI连接
jdbc.core.JdbcTemplate
JDBC连接
jdbc.core.namedparam.NamedParameterJdbcTemplate
支持命名参数的JDBC连接
jdbc.core.simple.SimpleJdbcTemplate
通过Java 5简化后的JDBC连接(Spring 3.1中已经废弃)
orm.hibernate3.HibernateTemplate
Hibernate 3.x以上的Session
orm.ibatis.SqlMapClientTemplate
iBATIS SqlMap客户端
orm.jdo.JdoTemplate
Java数据对象(Java Data Object)实现
orm.jpa.JpaTemplate
Java持久化API的实体管理器

以下三个是比较常用的:

  • JDBCJdbcTemplate
  • Hibernate:HibernateTemplate(最流行的基于POJOORM方案)
  • JPA:JpaTemplate(最流行的基于POJOORM方案)

4、准备

但首先要说明的是Spring所支持的大多数持久化功能都依赖于数据源。

3.1 配置数据源

1、目的
 
通过数据源建立了与数据库的连接
 
2、Spring配置数据源
spring提供了在Spring上下文中配置数据源bean的多种方式,包括:
  • 通过JDBC驱动程序定义的数据源:无连接池、适合小应用
  • 通过JNDI查找的数据源;(最好)
  • 数据库连接池的数据源。(其次)
    • Apache Commons DBCP
    • c3p0
    • BoneCP
  • 【嵌入式】的数据源:测试、开发

3.1.1 使用【JNDI】数据源

1、前提

使用web服务器时(如tomcat),web服务器允许你配置通过JNDI获取数据源。

2、好处

  • 数据源完全可以在应用程序之外进行管理,这样应用程序只需在访问数据库的时候查找数据源就可以了。
  • 在应用服务器中管理的数据源通常以池的方式组织,从而具备更好的性能
  • 支持系统管理员对其进行热切换

3、使用方式

利用Spring,我们可以像使用Spring bean那样配置JNDI中数据源的引用并将其装配到需要的类中。
 
1)XML方式
jee命名空间下的<jee:jndi-lookup>元素可以用于检索JNDI中的任何对象(包括数据源)并将其作为Springbean。如果应用程序的数据源配置在JNDI中,可以使用<jee:jndi-lookup>元素将其装配到Spring中,如下

其中jndi-name属性用于指定JNDI中资源的名称。如果只设置了jndi-name属性,那么就会根据指定的名称查找数据源。但是,如
果应用程序运行在Java应用服务器中,你需要将resource-ref属性设置为true,这样给定的jndi-name将会自动添加“java:comp/env/前缀。
 
2)JavaConfig方式
其中JndiObjectFactoryBean是spring内置提供的

3.1.2 使用【数据库连接池】数据源

1、说明

  • 这些都是第三方数据库连接池作为数据源
  • 如果无法使用JNDL配置数据源,其次选择在spring中配置数据库连接池数据源,spring没有提供实现,但是可以通过集成下面连接池进行实现:
    • Apache Commons DBCP
    • c3p0
    • BoneCP
2、使用
2.1 配置apache的DBCP数据源
依赖包:
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-dbcp</artifactId>
    <version>7.0.47</version>
</dependency>
1)XML形式
2)JavaConfig形式

前四个属性是必需的:

  • driverClassName指定了JDBC驱动类的全限定类名。这里配置的是H2数据库的数据源。
  • url用于设置数据库的JDBCURL。最
  • usernamepassword用于在连接数据库时进行认

其他属性如下表:

initialSize
池启动时创建的连接数量
maxActive
同一时间可从池中分配的最多连接数。如果设置为0,表示无限制
maxIdle
池里不会被释放的最多空闲连接数。如果设置为0,表示无限制
maxOpenPreparedStatements
在同一时间能够从语句池中分配的预处理语句(prepared statement)的最大数量。如果设置为0,表示无限制
maxWait
在抛出异常之前,池等待连接回收的最大时间(当没有可用连接时)。如果设置为-1,表示无限等待
minEvictableIdleTimeMillis
连接在池中保持空闲而不被回收的最大时间
minIdle
在不创建新连接的情况下,池中保持空闲的最小连接数
poolPreparedStatements
是否对预处理语句(prepared statement)进行池管理(布尔值)

2.2 配置C3P0数据源

依赖包:

<dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
</dependency>

XML形式

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

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://192.168.5.xxx:3306/ml_test"/>
        <property name="user" value="root"/>
        <property name="password" value="xxxx"/>
    </bean>
</beans>

其中value部分可以引用外部配置文件:

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <context:property-placeholder location="db/db.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

其中通过<context:property-placeholder location="db/db.properties"/>引入外部配置文件后,通过${xxx}的方式使用

3.1.3 使用【基于JDBC驱动】的数据源

1、说明:使用简单,无连接池,适用场景有限(小应用,开发环境)

2、种类:

  • org.springframework.jdbc.datasource.DriverManagerDataSource:每次返回新连接,无池化管理
  • org.springframework.jdbc.datasource.SimpleDriverDataSource:直接使用JDBC驱动,来解决在特定环境下的类加载问题,这样的环境包括OSGi容器
  • org.springframework.jdbc.datasource.SingleConnectionDataSource:在每个连接请求时都会返回同一个的连接

3、使用

3.1.4 使用【嵌入式】的数据源

1、作用(适用场景)

开发 和 测试时,好处:每次重启应用或运行测试的时候,都能够重新填充测试数据

2、使用:

以下配置会预先加载一组测试数据:

  • h2数据库位于类路径下
  • type配置为H2为使用H2数据库,配置成DERBY为使用derby数据库
  • 可以不配置也可以配置多个<jdbc:script>元素来搭建数据库,上面的配置包含了两个<jdbc:script>元素:第一个引用了schema.sql,它包含了在数据库中创建表的SQL;第二个引用了test-data.sql,用来将测试数据填充到数据库中。
  • <jdbc:embedded-database>元素还会暴露一个数据源,我们可以像使用其他的数据源那样来使用它。 在这里,id属性被设置成了dataSource,这也是所暴露数据源的bean ID。因此,当我们需要javax.sql.DataSource的时候,就可以注入dataSource bean
使用javaConfig方法配置:

3.2 选择数据源

1、使用场景:

在某种环境下需要其中一种数据源,而在另外的环境中需要不同的数据源
 
2、方案
 
使用系列文章springframework IOC一文中提到的profile机制,将每个数据源配置在不同的profile
3、使用
1)javaConfig方式
package com.mzj.springframework.dao._01_dataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;

import javax.sql.DataSource;

/**
 * @Auther: mazhongjia
 * @Date: 2020/4/20 13:07
 * @Version: 1.0
 */
@Configuration
public class DataSourceConfiguration {

    /**
     * 开发数据源
     *
     * @return
     */
    @Profile("development")
    @Bean
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:test-data.sql")
                .build();
    }

    /**
     * 生成环境的数据源
     *
     * @return
     */
    public DataSource dataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/SpittrDS");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
}

2)XML方式

 
4、结论
通过使用profile功能,会在运行时选择数据源,这取决于哪一个profile处于激活状态。
  • 当且仅当developmentprofile处于激活状态时,会创建嵌入式数据库;
  • 当且仅当qa profile处于激活状态时,会创建DBCPBasicDataSource;
  • 当且仅当productionprofile处于激活状态时,会从JNDI获取数据源

3.3 使用Spring的JDBC框架访问数据库

方式1:使用JDBC访问数据库

  • 优点
    • 低层次,完全控制如何读取和管理数据
    • 使用JDBC能够更好地对数据访问的性能进行调优
    • JDBC允许使用数据库所有特性
  • 缺点
    • 业务无关模式化代码、冗长,如资源申请、资源释放、异常处理
    • 不知道SQLException异常的具体原因,不知道如何处理

方式2:使用持久层框架访问数据库

  • 引入:清理资源和异常处理,非常重要,要保证正确性、健壮性。
  • Spring的JDBC框架作用:承担了资源管理和异常处理的工作,从而简化了JDBC代码,让我们只需编写从数据库读写数据的必需代码。

Spring的JDBC框架使用

1、spring的JDBC模板

     1)三种可选的模板

JdbcTemplate
最基本的Spring JDBC模板,这个模板支持简单的JDBC数据库访问功能以及基于索引参数的查询
绝大多数时候使用
NamedParameterJdbcTemplate
使用该模板类执行查询时可以将值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数;
只有在你需要使用命名参数的时候,才需要使用
SimpleJdbcTemplate
该模板类利用Java 5的一些特性如自
动装箱、泛型以及可变参数列表来简化JDBC模板的使用
Spring 3.1开始被废弃

2、JdbcTemplate使用

步骤1:定义DataSource

略,见前文。

步骤2:声明JdbcTemplate的bean,并向其注入DataSource

JavaConfig方式:

DataSource是通过构造器参数注入进来的。这里所引用的dataSourcebean可以是javax.sql.DataSource的任意实现,比如:

XML方式:

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <context:property-placeholder location="db/db.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

步骤3:将jdbcTemplate装配到Repository(业务DAO)中并使用它来访问数据库

/**
   * Repository1 bean(DAO),依赖JdbcTemplate
   * @param jdbcTemplate
   * @return
   */
  @Bean
  public SpitterRepository spitterRepository(JdbcTemplate jdbcTemplate) {
    return new JdbcSpitterRepository(jdbcTemplate);
  }

步骤4:SpitterRepository是接口,需要实现之,即JdbcSpitterRepository

比如insert:

        public Spittle save(Spittle spittle) {
		long spittleId = insertSpittleAndReturnId(spittle);
		return new Spittle(spittleId, spittle.getSpitter(), spittle.getMessage(), spittle.getPostedTime());
	}

	private long insertSpittleAndReturnId(Spittle spittle) {
			SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate).withTableName("Spittle");
			jdbcInsert.setGeneratedKeyName("id");
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("spitter", spittle.getSpitter().getId());
			args.put("message", spittle.getMessage());
			args.put("postedTime", spittle.getPostedTime());
			long spittleId = jdbcInsert.executeAndReturnKey(args).longValue();
			return spittleId;
	}
  • 已经没有创建、释放、连接的样板代码:样板代码被巧妙地隐藏到JDBC模板类中了
  • 已经没有SQLException处理的代码:在内部,JdbcTemplate将会捕获所有可能抛出的SQLException,并将通用的SQLException转换为表10.1所列的那些更明确的数据访问异常,然后将其重新抛出。因为Spring的数据访问异常都是运行时异常,所以我们不必在save()方法中进行捕获

3.4 使用Spring集成ORM框架访问数据库

1、ORM框架:

  • Hibernate
  • MyBatis
  • JDO,(Java Data Objects),Java数据对象
  • JPA、(Java Persistence API)、Java持久化API
2、使用ORM框架可以实现
  • 延迟加载:复杂的对象关系,用到时才真正加载
  • 预先抓取:使用一次查询获取完整的关联对象
  • 级联:更改数据库中的表会同时修改其他表

3、ORM框架可以提供

  • 支持集成Spring声明式事务;
  • 透明的异常处理;
  • 线程安全的、轻量级的模板类;
  • DAO支持类;
  • 资源管理

3.4.1 使用Spring集成Hibernate框架访问数据库

1、支持特性

  • 缓存
  • 延迟加载
  • 预先抓取
  • 分布式缓存

2、使用说明

直接使用Hibernate,不在spring中使用时
(依赖关系:SessionFactory创建Session实现,业务Repository依赖  Session实现)
  • Session接口提供了基本的数据访问功能,如保存、更新、删除以及从数据库加载对象的功能
  • SessionFactory主要负责创建Session接口的实现以及Session的打开、关闭以及管理

Spring中使用时,需要通过spring提供的HibernateSession工厂bean实现

(依赖关系:Spring的SessionFactoryBean依赖DataSource,业务Repository依赖  SessionFactory)

通过Spring的某一个SessionFactoryBean来获取Hibernate的SessionFactory,Spring提供的Hibernate Session工厂bean有三类:

  • org.springframework.orm.hibernate3.LocalSessionFactoryBean:Hibernate使用的是3.2-4之间版本(不包括4)+ 使用XML定义映射
  • AnnotationSessionFactoryBean:Hibernate使用的4以下的版本(不包括4)+ 使用注解方式定义持久
  • org.springframework.orm.hibernate4.LocalSessionFactoryBean:Hibernate使用4及以上版本 + 支持基于XML的映射和基于注解(通过JPA@Entity或@MappedSuperclass以及Hibernate@Entity的映射

这里只列出与hibernate4集成代码示例:

1)增加依赖:

<springframe.version>4.3.3.RELEASE</springframe.version>

<!--基础依赖-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframe.version}</version>
</dependency>
<!--集成hibernate依赖-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${springframe.version}</version>
</dependency>
<dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.1.6.Final</version>
</dependency>
<dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
</dependency>
<!--测试数据库依赖-->
<dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.182</version>
</dependency>

2)Spring应用上下文中配置【数据源】以及【spring提供的HibernateSession工厂bean】

3)创建自己的(业务)Repository类了

方式一:SpringHibernate的早期版本,使用HibernateTemplate

       缺点:Repository实现会直接与Spring耦合

方式二:使用上下文SessionContextual session

原理:会直接将Hibernate的SessionFactory装配到业务Repository中,并使用它来获取Session,在Repository中不会看到spring的影子

优点:Repository类及实现不与spring偶合(除了@Repository注解以外.....)

代码见:com.mzj.springframework.dao._03_hibernate._4子包中代码。

3.4.2 使用Spring集成JPA访问数据库

1、说明

JPA:Java持久化API

JPA是下一代Java持久化标准,JPA基于POJO的持久化机制,JPA借鉴了Hibernate+Java数据对象(JDO)+Java5的注解

2、Spring集成JPA传统方式(开发麻烦(业务Repository中使用EntityManager操作数据库),过时的,了解即可,后面的Spring Data JPA实现原理没变,只不过Spring给我们简化了这个过程)

原理:使用EntityManagerFactory(实体管理器工厂)的实现类来获取EntityManager(实体管理器)实例

EntityManager的创建与管理有两种方式(关键的区别不在于EntityManager本身,而是在于EntityManager的创建和管理方式):

  • 应用程序管理类型:适用于非JAVAEE程序(程序直接向实体管理器工厂请求实体管理器,EntityManagerFactory创建EntityManager,程序负责打开或关闭实体管理器、在事务中对其进行控制
  • 容器管理类型:适用于JAVAEE程序(实体管理器由Java EE创建和管理、实体管理器直接通过注入或JNDI来获取,应用程序根本不与实体管理器工厂打交道)

Spring都会负责管理EntityManager。如果你使用的是应用程序管理类型的实体管理器,Spring承担了应用程序的角色并以透明的方式处理EntityManager。在容器管理的场景下,Spring会担当容器的角色。

这两种实体管理器工厂分别由对应的Spring工厂Bean创建:

  • LocalEntityManagerFactoryBean生成应用程序管理类型EntityManager-Factory
  • LocalContainerEntityManagerFactoryBean生成容器管理类型的EntityManagerFactory
选择应用程序管理类型的还是容器管理类型的EntityManager Factory,对于基于Spring的应用程序来讲是完全透明的,唯一的区别在于如何在spring中进行配置


使用方式:

1)spring上下文中配置实体管理器工厂LocalEntityManagerFactoryBean

略。
2)编写基于JPA的业务Repository

略。

3、Spring Data JPA(自动化的Spring集成JPA实现方式)

 优点:只编写Repository接口就可以了。根本就不再需要实现类了。

 使用

1)增加依赖

<springframe.version>4.3.3.RELEASE</springframe.version>

<!--基础依赖-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframe.version}</version>
</dependency>

<!--集成springdata-jpa依赖-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${springframe.version}</version>
</dependency>
<dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>1.3.2.RELEASE</version>
</dependency>
<dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>4.0.1.Final</version>
</dependency>
<dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
</dependency>
<!--测试数据库依赖-->
<dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.182</version>
</dependency>
2)编写Spring Data JPA Repository接口
编写Repository接口关键是要从一组接口中挑选一个进行扩展。这里,SpitterRepository扩展了Spring Data JPA的JpaRepository
package com.mzj.springframework.dao._04_springdata_jpa.db;

import java.util.List;

import com.mzj.springframework.dao._04_springdata_jpa.vo.Spitter;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Repository interface with operations for {@link Spitter} persistence.
 * @author habuma
 */
public interface SpitterRepository extends JpaRepository<Spitter, Long> {
	  
	Spitter findByUsername(String username);
	
	List<Spitter> findByUsernameOrFullNameLike(String username, String fullName);

}
  • 通过给出泛型类型<Spitter,Long>,JpaRepository进行了参数化,使他能知道这是一个用来持久化Spitter对象的Repository,并且SpitterID类型为Long
  • 另外,它还会自动继承18个执行持久化操作的通用方法,如保存Spitter、删除Spitter以及根据ID查询Spitter
3)不需要编写Repository接口的实现Spring Data已经为我们做好了这件事请,但是要在spring中声明一下启动Spring Data JPA
方式一:JavaConfig的方式:
在Javaconfig类上增加如下注解:
@Configuration
@EnableJpaRepositories("com.mzj.springframework.dao._04_springdata_jpa")

      会根据这个包路径进行扫描,扫描它的基础包及其子包来查找扩展自Spring Data JPA Repository接口的所有接口。如果发现了扩展自Repository的接口,它会自动生成(在应用启动的时候)这个接口的实现(在Spring的应用上下文创建的时候生成的)。

方式二:XML的方式

4)如果自带的18个方法不够,可以添加自定义方法

方式一:在业务Repository接口中按照下面的DSL【语法规则】增加自定义查询

原理:当创建Repository实现的时候,Spring Data会检查Repository接口的所有方法,解析方法的名称,并基于被持久化的对象来试图推测方法的目的。本质上,Spring Data定义了一组小型的领域特定语言(domain-specific language ,DSL),在这里,持久化的细节都是通过Repository方法的签名来描述的。

语法规则:
由一个动词、一个可选的主题 (Subject)、关键词By以及一个断言所组成。
  • Spring Data允许在方法名中使用四种动词:getreadfindcount。其中,动词getreadfind是同义的,这三个动词对应的Repository方法都会查询数据并返回对象。而动词count则会返回匹配对象的数量,而不是对象本身
  • 主题是可选的。它的主要目的是让你在命名方法的时候,有更多的灵活性,对于大部分场景来说,主题会被省略掉。readSpittersByFirstnameOrLastname()与readPuppiesByFirstnameOrLastname()并没有什么差别
  • 要查询的对象类型是通过如何参数化JpaRepository接口来确定的,而不是方法名称中的主题
  • 在省略主题的时候,有一种例外情况。如果主题的名称以Distinct头的话,那么在生成查询的时候会确保所返回结果集中不包含重复记录。
  • 断言:用于限制结果集,在readByFirstnameOrLastname()这个样例中,会通过firstname或lastname属性的值来限制结果。
    • 在断言中,会有一个或多个限制结果的条件。每个条件必须引用一个属性,并且还可以指定一种比较操作。如果省略比较操作符的话,那么这暗指是一种相等比较操作
    •  
      比较操作有(下面不全):
  • 要对比的属性值就是方法的参数,比如:

  • 处理String类型的属性时,条件中可能还会包含【IgnoringCase】或【IgnoresCase】,这样在执行对比的时候就会不再考虑字符是大写还是小写
  • 方法参数的名称是无关紧要的,但是它们的顺序必须要与方法名称中的操作符相匹配。
  • 可以在方法名称的结尾处添加【OrderBy】,实现结果集排序,如果要根据多个属性排序的话,只需将其依序添加到OrderBy中即可,升序【Asc】,降序【Desc】

  • 条件部分是通过【And】或者【Or】进行分割的
findByUsername()这个样例中,动词是find,断言是Username,主题并没有指定,暗含的主题是Spitter

完整的示例为:

一句话总结:使用属性名和关键字构建Repository方法签名,就能让Spring Data JPA生成方法实现,完成几乎所有能够想象到的查询。Spring Data这个小型的DSL依旧有其局限性,有时候通过方法名称表达预期的查询很烦琐,甚至无法实现。如果遇到这种情形的话,Spring Data能够让我们通过@Query注解来解决问题。

方式二:通过@Query注解添加(声明)自定义查询

使用场景1:

如果所需的数据无法通过方法名称进行恰当地描述,那么我们可以使用@Query注解,为Spring Data提供要执行的查询。

 
使用场景2:
如果按照命名约定,方法的名称特别长的时候,也可以使用这个注解
使用方式:
其中@Query中的参数是实现业务查询的SQL语句                                                      

局限性:

@Query注解仅限於单个JPA查询。如果我们需要更为复杂的功能,无法在一个简单的查询中处理的话,使用Spring Data JPA的混合自定义的功能
 
方式三:使用混合自定义的功能
使用场景:复杂查询时,@Query注解无法解决
原理:当Spring Data JPA为Repository接口生成实现的时候,它还会查找名字与接口相同,并且添加了Impl后缀的一个类。如果这个类存在的话(我们自己创建的话),Spring Data JPA将会把它的方法与Spring Data JPA自动生成的方法合并在一起。
 
混合自定义的功能使用:
  • 按照spring data jpa约定我们自己创建接口的实现类
    • 约定:Repository接口的实现类名叫【Repository接口Impl】,但是此类不需要implements Repository接口
    • 为了让使用Repository接口的代码能通过Repository接口看到这个自定义方法,需要再单独定义一个接口,将准备实现的方法放入接口中,然后让【Repository接口Impl】实现这个接口,并让【Repository接口】也extends这个接口

  • 在【Repository接口Impl】中注入EntityManager(通过@PersistenceContext注解注入)
  • 【Repository接口Impl】实现这个接口的方法实现中,通过EntityManager实现复杂查询

其他说明:Impl后缀只是默认的做法,如果你想使用其他后缀的话,只需在配置@EnableJpa-Repositories的时候,设置repositoryImplementationPostfix属性即可

XML中使用<jpa:repositories>元素来配置Spring Data JPA的话,我们可以借助repository-impl-postfix属性指定后缀

5)使用JPA注解或者hibernate JPA注解对实体类属性进行声明以进行ORM映射

参考:https://blog.csdn.net/tianya846/article/details/81053343?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

或者《Spring Data Jpa 从入门到精通》

 

市场上 ORM 框架比对

MyBatis:MyBatis 本是 Apache 的一个开源项目 iBatis,2010 年这个项目由 Apache Software Foundation 迁移到了 Google Code,并且改名为 MyBatis,其着力于 POJO 与 SQL 之间的映射关系,可以进行更为细致的 SQL,使用起来十分灵活、上手简单、容易掌握,所以深受开发者的喜欢,目前市场占有率最高,比较适合互联应用公司的 API 场景;缺点就是工作量比较大,需要各种配置文件的配置和 SQL 语句。
Hibernate:Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库,并且对象有自己的生命周期,着力点对象与对象之间关系,有自己的 HQL 查询语言,所以数据库移植性很好。Hibernate 是完备的 ORM 框架,是符合 JPA 规范的,有自己的缓存机制,上手来说比较难,比较适合企业级的应用系统开发。
Spring Data JPA:可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现,引用 JPQL(Java Persistence Query Language)查询语言,属于 Spring 的整个生态体系的一部分。由于 Spring Boot 和 Spring Cloud 在市场上的流行,Spring Data JPA 也逐渐进入大家的视野,他们有机的整体,使用起来比较方便,加快了开发的效率,使开发者不需要关系和配置更多的东西,完全可以沉浸在 Spring 的完整生态标准的实现下,上手简单、开发效率高,又对对象的支持比较好,又有很大的灵活性,市场的认可度越来越高。
OpenJPA :是 Apache 组织提供的开源项目,它实现了 EJB 3.0 中的 JPA 标准,为开发者提供功能强大、使用简单的持久化数据管理框架,但功能、性能、普及性等方面更加需要加大力度,所以用的人不人不是特别多。
QueryDSL:QueryDSL 可以在任何支持的 ORM 框架或者 SQL 平台上以一种通用的 API 方式来构建查询,目前 QueryDSL 支持的平台包括 JPA、JDO、SQL、Java Collections、RDF、Lucene、Hibernate Search,同时 Spring Data JPA 也对 QueryDSL 做了很好的支持。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章