编辑推荐: |
本文主要讲解SpringData
概述 ,db.properties 文件 ,配置 JPA 的 EntityManagerFactory,配置
JPA 的事务管理器和支持注解的事务等希望对您能有所帮助。
本文来自于csdn,由火龙果软件Delores编辑推荐 |
|
一、SpringData 概述
1. SpringData:Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。
2.JPA Spring Data:致力于减少数据访问层 (DAO) 的开发量. 开发者唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA 来完成!
二、Spring Data JPA 的 Helloworld
1.环境搭建:
1)新建 Java Project:springdata
2)当前工程下新建 lib 目录
3)加入 Spring 的 jar 包:spring-framework-4.0.0.RELEASE\required 目录下所有
4)加入 Hibernate 和 JPA 的 jar 包:hibernate-release-4.2.4.Final\lib\required 和 hibernate-release-4.2.4.Final\lib\jpa 目录下的所有
5)加入 c3p0 和 MqSQL 的驱动
6)手动进行 Build Path
2.新建 Spring 的配置文件 applicationContext.xml,并进行数据源的配置
1) db.properties 文件
jdbc.user=root
jdbc.password=qiqingqing
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:// localhost:3306/springdata |
2)applicationContext.xml 文件中:
<!-- 配置自动扫描的包
--> <context:component-scan base-package ="com.software.springdata"> </context:component-scan>
<!-- 1.配置数据源 -->
<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> |
3)注意:希望大家能够养成良好的单元测试的习惯,以便及时发现问题,及时解决:测试数据源是否获取成功,新建一个 SpringDataTest 单元测试类
private ApplicationContext
ctx = null;
{
ctx = new ClassPathXmlApplicationContext ("applicationContext.xml");
}
//测试数据源是否获取成功
@Test
public void testDataSource() throws SQLException
{
DataSource dataSource = ctx. getBean(DataSource.class);
System.out.println (dataSource.getConnection());
} |
只要输出数据库连接的信息,那就证明我们数据源的的配置没有问题了。
3.配置 JPA 的 EntityManagerFactory
<!-- 2.配置
JPA 的 EntityManagerFactory --> <bean
id="entityManagerFactory"
class="org.springframework.orm.jpa. LocalContainerEntityManagerFactoryBean">
<!-- 配置数据源 --> <property name="dataSource"
ref="dataSource" ></property>
<!-- 配置 JPA 提供商的适配器 --> <property
name="jpaVendorAdapter"> <bean
class="org.springframework.orm. jpa.vendor.HibernateJpaVendorAdapter"></bean>
</property> <!-- 配置实体类所在包 -->
<property name="packagesToScan" value="com.software.springdata"></property>
<!-- 配置 JPA 实现产品的基本属性 --> <property
name="jpaProperties"> <props>
<!-- 生成的数据表的列的映射策略 --> <prop key="hibernate.ejb.naming_strategy"> org.hibernate.cfg.ImprovedNamingStrategy</prop>
<!-- hibernate 基本属性 --> <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> |
测试 JPA 是否配置成功:
新建一个实体类 Person,添加必要的 JPA 注解,创建一个空的 JPA 的测试方法,执行此方法,查看是否生成数据表
package com.software.springdata;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Table(name="JPA_PERSONS")
@Entity
public class Person {
private Integer id;
private String lastName;
private String email;
private Date birth;
private Integer addressId;
@Column(name="ADD_ID")
public Integer getAddressId() {
return addressId;
}
public void setAddressId (Integer addressId)
{
this.addressId = addressId;
}
private Address address;
@JoinColumn(name="ADDRESS_ID")
@ManyToOne
public Address getAddress() {
return address;
}
public void setAddress (Address address) {
this.address = address;
}
@GeneratedValue
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="LAST_NAME")
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;
}
@Temporal(TemporalType.DATE)
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 + ", birth=" + birth + "]";
}
} |
4.配置 JPA 的事务管理器和支持注解的事务
<!-- 3.配置
JPA 的事务管理器 --> <bean id="transactionManager"
class="org.springframework.orm.jpa. JpaTransactionManager">
<property name="entityManagerFactory"
ref="entityManagerFactory"></property>
</bean> <!-- 4.配置支持注解的事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/> |
5.配置 SpringData
1)加入 SpringData 的 jar 包:spring-data-jpa\required 目录下所有(自创建的目录),手动 Build Path
还要加入 SpringData 依赖的日志包 slf4j
2)加入 jpa 的命名空间,左下角 Namespaces 中添加
配置 SpringData 扫描的包和 EntityManagerFactory
<!-- 5.配置
Spring Data --> <!-- 加入 jpa 的命名空间:左下角
Namespaces 中加入 --> <!-- 配置 Spring Data
扫描的包和 EntityManagerFactory --> <!--
base-package:扫描 Repository Bean 所在的 package -->
<jpa:repositories base-package= "com.software.springdata"
entity-manager-factory-ref= "entityManagerFactory"></jpa:repositories> |
6.编写 SpringData 的代码,测试 HelloWorld
1)创建一个 PersonRepsotory 接口继承 Repository 接口,声明一个方法
2)编写测试方法:需要在测试类中引入 PersonRepository 的实例,通过 ctx.getBean(PersonRepository.class); 获取,以后不再赘述。
//测试 Spring Data
的 Helloworld
@Test
public void testSpringDataHelloworld() {
Person person = repository. getByLastName("AA");
System.out.println(person);
} |
这样便会获取 LastName 为 AA 的数据库中的那条记录。
三、Repository 接口
1. Repository 是一个空接口,即是一个标记接口。源码
2.若我们定义的接口实现了 Repository,则该接口会被 IOC 容器识别为一个 Repository Bean,纳入到 IOC 容器中,进而可以在该接口中定义一些符合规范的方法
3.还可以通过 @RepositoryDefinition 注解来替代继承 Repository 接口
4.Repository 接口的实现类:
1)CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
2)PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
3)JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
4)自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
注:JpaSpecificationExecutor: 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法
四、SpringData 的方法定义规范SpringData 的方法定义规范
1.不能随便声明,需要符合一定的规范
2.查询的方法以 find、read、get 开头
3.涉及条件查询时条件的属性要以关键字连接,条件属性首字母大写
1)SpringData 支持的关键字
2)举例:
//WHERE lastName
like %? AND id < ?
List<Person> getByLastNameEnding WithAndIdLessThan (String
lastName, Integer age);
//WHERE lastName like ?% AND id > ?
List<Person> getByLastNameStarting WithAndIdGreaterThan (String
lastName, Integer age);
//WHERE email IN(?,?,?) OR birth < ?
List<Person>
getByEmailInOrBirthLessThan
(List<String> emails, Date date); |
测试:
//测试 SpringData
支持的关键字
@Test
public void testKeyWords() {
List<Person> persons = repository. getByLastNameEnding WithAndIdLessThan("A",
5);
System.out.println(persons);
persons = repository.getByLastName StartingWithAndIdGreaterThan("A",
1);
System.out.println(persons);
persons = repository.getByEmailInOrBirthLessThan (Arrays.asList("aa@163.com",
"cc@163.com",
"ee@163.com"), new Date());
System.out.println(persons.size());
} |
4.支持级联查询,若当前类有符合条件的查询,则优先使用,而不使用级联查询,若需要使用级联查询,则属性之间需要使用 _ (下划线)来连接
1)创建 Address 类,添加对应的 JPA 注解,在 Person 类中添加 Address 类的引用,使用 @ManyToOne 映射,生成对应的数据表
2)创建测试方法:此时为级联查询
3)为 Person 类添加 addressId 属性后,若再进行相同的测试,则不是级联查询了,而查询条件为 Person 类中的 addressId 属性。若要再进行级联查询,需要在属性之间需要使用 _ (下划线)来连接
五、@Query 注解
1.使用 @Query 注解可以自定义 JPQL 语句实现更加灵活的查询
定义方法:
//查询 id 值最大的
Person
//使用 @Query 注解可以自定义 JPQL 语句实现更加灵活的查询
@Query("SELECT p FROM Person p WHERE p.id
= (SELECT max(p2.id) FROM Person p2)")
Person getMaxIdPerson(); |
测试方法:
//测试 @Query 注解
@Test
public void testQueryAnnotation() {
Person person = repository. getMaxIdPerson();
System.out.println(person);
} |
2.为 @Query 注解传递参数的两种方式
1)使用占位符:
//为 @Query 注解传递参数的方式1: 使用占位符,
此方式要求形参与定义的 JPQL 的参数位置一致
@Query("SELECT p FROM Person p WHERE p.lastName
= ?1 AND p.email = ?2")
List<Person> testQueryAnnotationParam1 (String
lastName, String email); |
测试:
//测试向 @Query
注解传参
@Test
public void testQueryAnnotationParam1(){
List<Person> persons = repository. testQueryAnnotationParam1 ("AA",
"aa@163.com");
System.out.println(persons);
} |
2)使用命名参数:
//为 @Query 注解传递参数的方式2: 使用命名参数,
此方式形参与定义的 JPQL 的参数位置不必一致
@Query("SELECT p FROM Person p WHERE p.lastName
= :lastName AND p.email = :email")
List<Person> testQueryAnnotationParam2 (@Param("email")
String email, @Param("lastName") String
lastName); |
//测试向 @Query
注解传参
@Test
public void testQueryAnnotationParam2() {
List<Person> persons = repository.testQueryAnnotationParam2 ("aa@163.com",
"AA");
System.out.println(persons);
} |
3.带有 LIKE 关键字的 @Query 注解
1)需要在测试方法中手动加上 %,不推荐
//带有 LIKE 关键字的
@Query 注解
@Query("SELECT p FROM Person p WHERE p.lastName
LIKE ?1 AND p.email LIKE ?2")
List<Person> testQueryAnnotationLikeParam (String
lastName, String email); |
//测试带有 LIKE 关键字的
@Query 注解
@Test
public void testQueryAnnotationLikeParam() {
List<Person> persons = repository. testQueryAnnotationLikeParam("%A%",
"%aa%");
System.out.println(persons);
} |
2)SpringData 允许在占位符上添加 %
//带有 LIKE 关键字的
@Query 注解: SpringData 允许在占位符上添加 %
@Query("SELECT p FROM Person p WHERE p.lastName
LIKE %?1% AND p.email LIKE %?2%")
List<Person> testQueryAnnotationLikeParam2 (String
lastName, String email); |
//测试带有 LIKE 关键字的
@Query 注解
@Test
public void testQueryAnnotationLikeParam2() {
List<Person> persons = repository. testQueryAnnotationLikeParam2("A",
"aa");
System.out.println(persons);
} |
3)SpringData 允许在命名参数上添加 %
//带有 LIKE 关键字的
@Query 注解: SpringData 允许在命名参数上添加 %
@Query("SELECT p FROM Person p WHERE p.lastName
LIKE %:lastName% AND p.email LIKE %:email%")
List<Person> testQueryAnnotationLikeParam3 (@Param("lastName")
String lastName, @Param("email") String
email); |
//测试带有 LIKE 关键字的
@Query 注解
@Test
public void testQueryAnnotationLikeParam3(){
List<Person> persons = repository.testQueryAnnotationLikeParam3 ("A",
"aa");
System.out.println(persons);
} |
4.使用 @Query 执行本地 SQL 查询,在 @Query 注解中添加参数 nativeQuery=true
//本地 SQL 查询:设置
nativeQuery=true 即可执行本地查询
@Query(value="SELECT count(id) FROM jpa_persons",
nativeQuery=true)
long getTotalCount(); |
//测试本地 SQL 查询
@Test
public void testNativeQuery() {
long count = repository.getTotalCount();
System.out.println(count);
} |
六、@Modifying 注解
1.使用 @Modifying 配合 @Query 可以完成 UPDATE 和 DELETE 操作
2.可以通过自定义 JPQL 完成 UPDATE 和 DELETE 操作,注:JPQL 不支持使用 INSERT
3.在 @Query 中编写 JPQL 语句,但必须使用 @Modifying 注解修饰,以通知 SpringData 此操作是一个 UPDATE 或 DELETE 操作
4.UPDATE 或 DELETE 操作需要使用事务,所以需要定义 service 层,在 service 层方法上添加事务操作
5.示例:
/*
* 可以通过自定义 JPQL 完成 UPDATE 和 DELETE 操作,注:JPQL 不支持使用
INSERT
* 在 @Query 中编写 JPQL 语句,但必须使用 @Modifying 注解修饰,以通知
SpringData 此操作是一个 UPDATE 或 DELETE 操作
* UPDATE 或 DELETE 操作需要使用事务, 所以需要定义 service 层,在
service 层 方法上添加事务操作
* 默认情况下,SpringData 的每个方法上都有事务, 但都是只读事务,他们不能完成修改操作
*/
@Modifying
@Query("UPDATE Person p SET p.email = :email
WHERE p.id = :id")
void updatePersonEmail(@Param("id")
Integer id, @Param("email") String email); |
七、Repository 接口的子接口
1、CrudRepository 接口;测试此接口的 save(Iterable< S > entities) 方法进行批量保存
1)使自定义的接口继承 CrudRepository 接口
2)Service 层:需要事务
3)测试:
2. PagingAndSortingRepository 接口
1)实现分页操作(带排序):(只读事务,不需要再 service 层中编写)
//测试 PagingAndSortRepository
的 findAll(Pageable pageable) 方法,进行分页
@Test
public void testPagingAndSortingRepository() {
//pageNo 从 0 开始
int pageNo = 3 - 1;
int pageSize = 5;
//Sort 封装了排序信息,Order 指明具体是根据哪一个属性进行升序或者降序
Order order1 = new Order(Direction.DESC, "id");
Order order2 = new Order(Direction.ASC, "email");
Sort sort = new Sort(order1, order2);
//Pageable 接口通常使用其 PageRequest 实现类,其中封装了需要分页的信息
PageRequest pageable = new PageRequest (pageNo,
pageSize, sort);
Page<Person> page = pagingAndSortingRepository. findAll(pageable);
System.out.println("总共有 " + page.getTotalElements()
+ " 条记录");
System.out.println("总共有 " + page.getTotalPages()
+ " 页");
System.out.println("当前页为:" + (page.getNumber()
+ 1));
System.out.println("当前的 List: " + page.getContent());
System.out.println("当前页的总记录数为:" + page.getNumberOfElements());
} |
3. JpaRepository 接口:测试此接口的 saveAndFlush() 方法,此方法相当于 JPA 中的 merge() 方法,详见 JPA 笔记
补:JpaSpecificationExecutor 接口,不属于Repository体系,实现一组 JPA Criteria 查询相关的方法 ,即带条件的分页查询
1)自定义的接口必须实现 Repository 或 Repository 的子接口 还有 JpaSpecificationExecutor 接口
八、自定义 Repository 方法:为某一个 Repository 上添加自定义方法
1.步骤:
1)定义一个接口: 声明要添加的, 并自实现的方法
2)提供该接口的实现类: 类名需在要声明的 Repository(以 PersonRepository 为例) 后添加 Impl, 并实现方法
3)声明 Repository 接口(即 PersonRepository 接口), 并继承 1) 声明的接口
4)测试使用:
|