前言
本篇文章主要记录本人在学习使用Spring框架组件Spring Data的过程中一些总结及感悟,学习过程中通过新建项目边学边练来加深理解
Spring Data 概述
- Spring Data是Spring 的一个子项目。用于简化数据库访问,支持NoSQL和关系数据存储 。其主要目标是使数据库的访问变得方便快捷。
- Spring Data 项目所支持NoSQL存储:
-MongoDB (文档数据库)
-Neo4j(图形数据库)
-Redis(键/值存储)
-Hbase(列族数据库) - Spring Data 项目所支持的关系数据存储技术:
-JDBC
-JPA(本次采用) - JPA Spring Data致力于减少数据访问层 (DAO) 的开发量. 开发者唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
- 框架怎么可能代替开发者实现业务逻辑呢?比如:当有一个 UserDao.findUserById() 这样一个方法声明,大致应该能判断出这是根据给定条件的 ID 查询出满足条件的 User 对象。Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。
搭建环境
练习项目采用Maven+Spring Data,具体jar包及环境配置此处略,可参照
Spring Data 、JPA进行持久层开发步骤
使用 Spring Data JPA 进行持久层开发需要的四个步骤:
配置 Spring 整合 JPA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <context:property-placeholder location="classpath: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="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean> </property> <property name="packagesToScan" value="com.syshlang"></property> <property name="jpaProperties"> <props>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"></property> </bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
|
在Spring 配置文件中配置 Spring Data
在Spring 配置文件中配置 Spring Data,让Spring为声明的接口创建代理对象。配置了 <jpa:repositories>后,Spring 初始化容器时将会扫描 base-package指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为Spring Bean,业务层便可以通过Spring自动封装的特性来直接使用该对象。
1 2 3 4 5 6
| <jpa:repositories base-package="com.syshlang" entity-manager-factory-ref="entityManagerFactory"> </jpa:repositories>
|
声明持久层的接口
声明持久层的接口,该接口继承Repository,Repository 是一个标记型接口,它不包含任何方法,如必要,Spring Data 可实现 Repository其他子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
持久层的接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
package com.syshlang.repository; import com.syshlang.entity.Person; import org.springframework.data.repository.Repository; public interface PersonRepsotory extends Repository<Person,Integer>{ }
|
实体类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
package com.syshlang.entity;
import javax.persistence.*; import java.util.Date;
@Table(name="JPA_PERSONS") @Entity public class Person {
private Integer id; private String lastName;
private String email; private Date birth;
@GeneratedValue @Id public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Date getBirth() { return birth; }
public void setBirth(Date birth) { this.birth = birth; }
@Override public String toString() { return "Person [id=" + id + ", lastName=" + lastName + ", email=" + email + ", brith=" + birth + "]"; } }
|
- Repository 是一个空接口. 即是一个标记接口;
- 若我们定义的接口继承了 Repository, 则该接口会被 IOC 容器识别为一个 Repository Bean.纳入到 IOC 容器中. 进而可以在该接口中定义满足一定规范的方法;
- 实际上, 也可以通过 @RepositoryDefinition 注解来替代继承 Repository 接口。
在接口中声明需要的方法
在接口中声明需要的方法,Spring Data 将根据给定的策略来为其生成实现代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
package com.syshlang.repository; import com.syshlang.entity.Person; import org.springframework.data.repository.Repository;
public interface PersonRepsotory extends Repository<Person,Integer>{
Person getByLastName(String lastName); }
|
测试:
1 2 3 4 5 6 7
| @Test public void testSpringData(){ PersonRepsotory personRepsotory = ctx.getBean(PersonRepsotory.class); Person person = personRepsotory.getByLastName("AA"); System.out.println(person); }
|
测试结果:
1 2 3 4 5 6 7 8 9 10 11
| Hibernate: select person0_.id as id1, person0_.birth as birth2, person0_.email as email3, person0_.last_name as last_nam4 from jpa_persons person0_ where person0_.last_name=? Person [id=1, lastName=AA, email=syshlang@163.com, brith=2018-06-07 13:42:02.0]
|
在 Repository 子接口中声明方法:
- 不是随便声明的. 而需要符合一定的规范;
- 查询方法以 find | read | get 开头;
- 涉及条件查询时,条件的属性用条件关键字连接;
- 要注意的是:条件属性以首字母大写;
- 支持属性的级联查询. 若当前类有符合条件的属性, 则优先使用, 而不使用级联属性;
- 若需要使用级联属性, 则属性之间使用 _ 进行连接。
附:配置清单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <?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: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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.syshlang"></context:component-scan>
<context:property-placeholder location="classpath: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="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean> </property> <property name="packagesToScan" value="com.syshlang"></property> <property name="jpaProperties"> <props>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"></property> </bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<jpa:repositories base-package="com.syshlang" entity-manager-factory-ref="entityManagerFactory"> </jpa:repositories>
</beans>
|