Spring Data JPA 学习笔记

一、JPA 介绍

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 一句话,JPA 只是一种接口、一种规范、比如Hibernate、OpenJPA 这些就属于Jpa的具体实现。

二、Spring Data JPA 解决的问题

Spring 为什么要弄出来这个Spring Data JPA 呢? 可以看看以下的代码,然后思考这些代码有什么弊端?

  • Dao层接口的定义
public interface UserDao{
  
  void save(User user);
  
  void delete(User user);
  
  ...
}
  • Dao具体的实现类

public class UserDaoImpl extends HibernateDaoSupport implements UserDao{
  
  public void save(User user){
    getHibernateTemplate().save(user);
  }
  
  public void delete(User user){
    getHibernateTemplate().delete(user);
  }
  
  ...
}

咋一看上,可能会说,没啥问题呀~多简单明了,别说我这种小学三年级的能看得懂,就算是小学四年级也能看得懂。 诚如这些想法一样,这样的代码已经很精简了,就一句话而已, 难道我们的dao的实现类能再精简么,变成什么都不写么? 这个想法确实还真被spring给想到了。

  • 问题所在
上述代码有两个问题:
1. 仔细观察两个具体实现方法, 不管以后是保存用户还是保存商品、或者是保存订单,都是一个模子刻出来的,唯一的不同只是具体操作的数据不同而已,当然删除也是一样。
2. 我们现在使用的是Hibernate 这一种ORM技术,如果以后我们想换成别的ORM 技术,那么需要修改代码。这就违反了`开-闭原则` 【对扩展开发、对修改关闭】 ,要不就是重新打造新的实现类,抛弃旧的实现类。

那么有没有一种方案,让我们能够以不变应万变 ,轻而易举解决掉上面的两个问题? Spring Data JPA是一个完美的解决方案。

在JavaEE 5.0发布的时候,sun公司就提出了jpa的规范,希望整合ORM技术,实现天下归一 。 虽然我们学过的orm技术只有hibernate、但是其实orm技术还有其他的一些方案,比如Apache的 openJPA。 Spring 作为业务逻辑层框架,起到承上启下的作用。所以它对JPA的这些技术实现做了一套封装。只要按照规范来配置即可,甚至你们的dao层实现都不用实现了,它在内部给你实现,如果我们想换到其他的jpa实现方案,那么只需要修改配置即可。

三、Spring Data JPA 下载

  • 官网地址

https://projects.spring.io/spring-data/

  • 包含的模块

Spring-data-commonsSpring-data-jpa

下面摘录官网对这两个模块的介绍

这个模块是所有Spring Data 系列工程的核心模块。

这个模块是专注于JPA这种操作的模块

Spring Data 还针对其他的不同类型技术提供支持比如: Spring Data RedisSpring Data JDBCSpring Data MongoDB

四、Spring Data JPA 入门

该小节,我们需要使用Spring 来配置 data jpa ,并且完成一个简单的查询所有操作。

1. 导入jar包
spring的jar包
	core | context | beans | expression 4个核心
	aop相关jar包 4个
	spring-orm-xxx.jar 、spring-jdbc-xxx.jar 、 spring-tx-xxx.jar 、spring-test-xxx.jar
	日志输出jar  4个


hibernate的jar包
	核心必须包
	
其实上面这些也不用看了,就和我们以前整合Spring + Hibernate一样。但是如果要想使用Spring Data JPA 我们需要额外导入以下的jar包

spring-data-commons.jar 、 spring-data-jpa.jar  、 hibernate-entitymanager-5.0.7.Final.jar
2. Dao层代码

这个CrudRepository 是spring已经针对JPA 定义好的一套增删改查的接口 , 我们继承这个接口即可。 实现类,我们也不用自己做了,后面可以让spring data jpa 帮忙创建这个接口的实现类

//继承接口,需要给定两个泛型,第一个User泛型是表示我们要查询的是哪一个表,这里写它对应的持久化类
//第二个泛型,写的是持久化类的主键类型 OID类型。
public interface UserDao extends CrudRepository<User, Integer> {

}

3. Service层代码
public interface UserService {

	List<User> findAll();
}

-------------------------------------------------

@Service
@Transactional // 其实这是查询的操作,开不开事务都可以运行。
public class UserServiceImpl implements UserService {

	@Autowired
	private UserDao userDao;
	
	@Override
	public List<User> findAll() {
		return (List<User>) userDao.findAll();
	}

}

4. 测试类代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
	
	@Autowired
	private UserService userService;
	
	@Test
	public void testFindAll(){
		System.out.println(userService.findAll());
	}
}

5. applicationContext.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:tx="http://www.springframework.org/schema/tx" 
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:jpa="http://www.springframework.org/schema/data/jpa"
        xsi:schemaLocation="
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
	        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
	        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 导入jdbc.properties文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 使用c3p0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${driverClass}"></property>
		<property name="jdbcUrl" value="${jdbcUrl}"></property>
		<property name="user" value="${user}"></property>
		<property name="password" value="${password}"></property>
	</bean>
	
	
	<context:component-scan base-package="com.unknow"/>
	
	<!-- 这里开始配置spring data jpa -->
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		
		<!-- 1. 设置如何连接数据库 -->
		<property name="dataSource" ref="dataSource"/>
		
		<!-- 2.可选的连接属性 -->
		<property name="jpaProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
			</props>
		</property>
		
		<!-- 3. 设置采用什么技术  指定适配器,其实如果我们想换成别的jpa实现技术,只需要替换这个位置即可-->
		<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
		
		<!-- 4. 指定映射文件 -->
		<property name="packagesToScan" value="com.unknow.bean"></property>
	</bean>
	
	<!-- 5. 指定我们的dao层在哪个位置,以便data jpa 创建这些接口的动态代理。 -->
	<jpa:repositories base-package="com.unknow.dao"/>

	<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>	
	
	<!-- 6. 开启事务 -->
	 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory"/>
	</bean>
	
	<!--如果只是查询的操作,这句话可以不用,但是上面的事务管理员必须声明,否则会报错。因为spring data里面会来检测是否有
	声明事务管理员,也不知道它用还是没用,总之它还是找了。 -->
	 <tx:annotation-driven transaction-manager="transactionManager"/> 
</beans>        

五、Spring Data JPA 接口介绍

经过上面的入门例子,也看到了,spring data对于我们编程确实方便了许多。我们无需关心dao层的实现,只需要简单的照着规则去写代码即可,spring data jpa 最重要的就是我们的dao层继承的接口。接下来着重说明它的几个接口。

1. Repository

这个接口是所有接口的父接口,也就是它就是老大了。所有我们后面用的接口都是从这位兄弟身上扩展来的。 下图是Repository 这个接口的继承体系,图中的UserDao 和 MyRepository 是我自己写的,不用理会。 不过要声明的是: 这个Repository 是一个空接口!!!,里面没有任何方法。

在这里插入图片描述

package org.springframework.data.repository;

import java.io.Serializable;
/**
 * Central repository marker interface. Captures the domain type to manage as well as the domain type's id type. General
 * purpose is to hold type information as well as being able to discover interfaces that extend this one during
 * classpath scanning for easy Spring bean creation.
 * <p>
 * Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the
 * same signature as those declared in {@link CrudRepository}.
 * 
 * @see CrudRepository
 * @param <T> the domain type the repository manages
 * @param <ID> the type of the id of the entity the repository manages
 * @author Oliver Gierke
 */
public interface Repository<T, ID extends Serializable> {

}

这个repository虽然是个空接口,我们自己来继承它,虽然没有任何要实现的方法,但是我们可以自己写,自己声明方法,这也是允许D~~ ,话不多说,走起~~~

  • 自定义接口 MyRepository
public interface MyRepository extends Repository<User, Integer> {

	/**
	 * 根据用户id查询用户
	 * @param uid
	 * @return
	 */
	User findByUid(int uid);

}
  • service层代码
public interface UserService {

	User findByUid(int uid);
}

------------------------------
@Service
@Transactional  //现在仍然是查询这个注解可以不写,为了免得记太多,我就索性都打开了。
public class UserServiceImpl implements UserService {	
	@Autowired
	private MyRepository repository;

	@Override
	public User findByUid(int uid) {
		return repository.findByUid(uid);
	}

}  
  
  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
	
	@Autowired
	private UserService userService;
  
	@Test
	public void testFindByUid(){
		System.out.println(userService.findByUid(1));
		
	}
}
  • 结果:
    在这里插入图片描述
  • 疑问:

写到这,有的绝对会有疑问, 这都可以,那这也行的话,我写一个叫做 fUid() 或者叫做 zhaoUserByUid() 这样的方法名,行不行呢? 答案是: NO!

因为我们声明的是接口,具体的实现类还是由人家的spring做出来的。spring胃口比较挑剔,而且为了满足大众化的口味,就做出了一些命名上的要求,要慢慢习惯这种要求,这也正是它的另一个框架Spring Boot的口号习惯优于配置 。 下图摘自 Spring Data JPA 文档对方法关键字的描述。

如:我们想按照用户的name 查询, 那么可以写成 findByName 以此类推

在这里插入图片描述
在这里插入图片描述

如果觉得这些关键字记不住,那么spring可以允许我们程序员自己定义自己的方法名。

2. 自定义方法名

这个小节主要讲述的是,我们可以自己定义自己的方法名,你爱叫啥叫啥,随意。

  • Dao接口
public interface MyRepository extends Repository<User, Integer> {


	@Query("from User  where uid = ?1") //?1 表示取第一个参数uid 来替代这个?。
	User selectUser(int uid);

}

  • Service
public interface UserService {

	User selectUser(int uid);
}

  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
	
	@Autowired
	private UserService userService;

	@Test
	public void testSelectUser(){
		System.out.println(userService.selectUser(1));
		
	}
}

虽然我们可以自己定义方法名,但是这种做法稍微有点麻烦,所以建议少用这种写法。 只有在以后执行多表查询的时候,我们才需要这么做。

3. CrudRepository

这个接口是扩展了repository, 在这个接口里面默认给大家声明好了一些增删改查的方法,此处给大家演练一个查询所有用户的操作

  • Dao
public interface UserDao extends CrudRepository<User, Integer> {
	//查询所有的用户
}

  • Service
@Service
//@Transactional
public class UserServiceImpl implements UserService {
	
	@Autowired
	private UserDao userDao;

	@Override
	public List<User> findAll() {
		return (List<User>) userDao.findAll();
	}

}

  • 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
	
	@Autowired
	private UserService userService;
	
	@Test
	public void testFindAll(){
		List<User> list= userService.findAll();
		System.out.println("list="+list);
	}
}

####4.PagingAndSortingRepository

这个接口是是CrudRepository的扩展接口,除了兼备增删改查之外,它还扩展了分页查询&排序的效果。

以下是这个接口的代码声明

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

	/**
	 * Returns all entities sorted by the given options.
	 * 
	 * @param sort
	 * @return all entities sorted by the given options
	 */
	Iterable<T> findAll(Sort sort);

	/**
	 * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
	 * 
	 * @param pageable
	 * @return a page of entities
	 */
	Page<T> findAll(Pageable pageable);
}
  • Dao
public interface UserDao extends PagingAndSortingRepository<User, Integer> {
	//查询所有的用户
}

  • Service
public interface UserService {
	
	
	/**
	 * 返回值是page 这个类型是springdatajpa定义好的。其实就和
	 * 我们自己平常写的PageBean 一样。
	 * @param pageable
	 * @return
	 */
	Page<User> findByPage(Pageable pageable);
}

---------------------------------------------------------
  
  @Service
//@Transactional
public class UserServiceImpl implements UserService {
	
	@Autowired
	private UserDao userDao;

	@Override
	public Page<User> findByPage(Pageable pageable) {
		return userDao.findAll(pageable);
	}

}
  • 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
	
	@Autowired
	private UserService userService;
	
	@Test
	public void testFindByPage(){
		//这里ctrl + T 看实现类,然后找PageRequest 
		// page : page表示请求的页码数, 从0开始,0就代表第一页。
		//size : 表示每页拿多少条记录
		Pageable pageable = new PageRequest(1, 2);
		Page<User> page = userService.findByPage(pageable);
		
      //当前是第几页 这个页码从0开始的,大家可以加上1表示从1开始。
		System.out.println(page.getNumber()); 
		System.out.println(page.getTotalPages()); //总页数
		System.out.println(page.getSize()); //每页个数
		System.out.println(page.getTotalElements()); //总记录数
		System.out.println(page.getContent()); //总页数
	}
}

六、

  • JpaReposityry&多表查询
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值