学习Spring Data JPA

简介

Spring Data 是spring的一个子项目,在官网上是这样解释的:

Spring Data 是为数据访问提供一种熟悉且一致的基于Spring的编程模型,同时仍然保留底层数据存储的特​​殊特性。它可以轻松使用数据访问技术,可以访问关系和非关系数据库。

简而言之就是让访问数据库能够更加便捷。

Spring Data 又包含多个子项目:

  • Spring Data JPA

  • Spring Data Mongo DB

  • Spring Data Redis

  • Spring Data Solr

本文章主要介绍Spring Data JPA的相关知识:

传统方式访问数据库

使用原始JDBC方式进行数据库操作

创建数据表:

917807-20170522091554898-1468234684.png

917807-20170522091604351-450682169.png

开发JDBCUtil工具类

获取Connection,释放资源,配置的属性放在配置文件中,通过代码的方式将配置文件中的数据加载进来。

package com.zzh.util;import java.io.InputStream;
import java.sql.*;
import java.util.Properties;public class JDBCUtil {public static Connection getConnection() throws Exception {InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");Properties properties = new Properties();properties.load(inputStream);String url = properties.getProperty("jdbc.url");String user = properties.getProperty("jdbc.user");String password = properties.getProperty("jdbc.password");String driverClass = properties.getProperty("jdbc.driverClass");Class.forName(driverClass);Connection connection = DriverManager.getConnection(url, user, password);return connection;}public static void release(ResultSet resultSet, Statement statement,Connection connection) {if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

建立对象模型和DAO

917807-20170522155703195-1084405773.png

package com.zzh.dao;import com.zzh.domain.Student;
import com.zzh.util.JDBCUtil;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;/*** Created by zhao on 2017/5/22.*/
public class StudentDAOImpl implements StudentDAO{/*** 查询学生*/@Overridepublic List<Student> query() {List<Student> students = new ArrayList<>();Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;String sql = "select * from student";try {connection = JDBCUtil.getConnection();preparedStatement = connection.prepareStatement(sql);resultSet = preparedStatement.executeQuery();Student student = null;while (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");int age = resultSet.getInt("age");student = new Student();student.setId(id);student.setAge(age);student.setName(name);students.add(student);}} catch (Exception e) {e.printStackTrace();}finally {JDBCUtil.release(resultSet,preparedStatement,connection);}return students;}/*** 添加学生*/@Overridepublic void save(Student student) {Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;String sql = "insert into student(name,age) values (?,?)";try {connection = JDBCUtil.getConnection();preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, student.getName());preparedStatement.setInt(2,student.getAge());preparedStatement.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally {JDBCUtil.release(resultSet,preparedStatement,connection);}}
}

可以看到基于原始的jdbc的编程,同一个dao方法中有太多的重复代码。


使用Spring JdbcTemplate进行数据库操作

Spring内置模板JdbcTemplate的出现大大提高了开发效率。

  • 创建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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><context:property-placeholder location="classpath:db.properties"/><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driverClass}"/><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/><property name="url" value="${jdbc.url}"/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean><bean id="studentDAO" class="com.zzh.dao.StudentDAOSpringJdbcImpl"><property name="jdbcTemplate" ref="jdbcTemplate"/></bean>
</beans>
  • 编写查询学生和保存学生方法
package com.zzh.dao;import com.zzh.domain.Student;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;public class StudentDAOSpringJdbcImpl implements StudentDAO{private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Overridepublic List<Student> query() {final List<Student> students = new ArrayList<>();String sql = "select * from student";jdbcTemplate.query(sql, new RowCallbackHandler() {@Overridepublic void processRow(ResultSet resultSet) throws SQLException {int id = resultSet.getInt("id");String name = resultSet.getString("name");int age = resultSet.getInt("age");Student student = new Student();student.setId(id);student.setAge(age);student.setName(name);students.add(student);}});return students;}@Overridepublic void save(Student student) {String sql = "insert into student(name,age) values (?,?)";jdbcTemplate.update(sql, new Object[]{student.getName(), student.getAge()});}
}

弊端分析

  • DAO中有太多的代码

  • DAOImpl有大量重复的代码

  • 开发分页或者其他的功能还要重新封装


Spring Data

用上面的两种方式进行数据库操作可以感受到代码的重复性和易用性都是有缺点的,现在先以一个小例子进行spring data操作数据库的基本示范,之后再逐步分析他的具体功能。

小例子

添加pom依赖:

<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-jpa</artifactId><version>1.8.0.RELEASE</version>
</dependency>
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId><version>4.3.6.Final</version>
</dependency>
  • 创建一个新的spring配置文件:beans-new.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: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.xsdhttp://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><context:property-placeholder location="classpath:db.properties"/><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driverClass}"/><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.password}"/><property name="url" value="${jdbc.url}"/></bean><!-- 配置EntityManagerFactory--><bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource" ref="dataSource"/><property name="jpaVendorAdapter"><bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/></property><property name="packagesToScan" value="com.zzh"/><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"/></bean><!--支持注解的事物--><tx:annotation-driven transaction-manager="transactionManager"/><!--spring data--><jpa:repositories base-package="com.zzh" entity-manager-factory-ref="entityManagerFactory"/><context:component-scan base-package="com.zzh"/>
</beans>

LocalContainerEntityManagerFactoryBean:

适用于所有环境的FactoryBean,能全面控制EntityManagerFactory配置,非常适合那种需要细粒度定制的环境。

jpaVendorAdapter:

用于设置JPA实现厂商的特定属性,如设置hibernate的是否自动生成DDL的属性generateDdl,这些属性是厂商特定的。目前spring提供HibernateJpaVendorAdapter,OpenJpaVendorAdapter,EclipseJpaVendorAdapter,TopLinkJpaVenderAdapter四个实现。

jpaProperties:

指定JPA属性;如Hibernate中指定是否显示SQL的“hibernate.show_sql”属性.

  • 建立实体类Employee:

917807-20170522201411335-1344436573.png

  • 自定义接口并继承Repository接口

继承的Repository接口泛型里的第一个参数是要操作的对象,即Employee;第二个参数是主键id的类型,即Integer。
方法即为根据名字找员工,这个接口是不用写实现类的。为什么可以只继承接口定义了方法就行了呢,因为spring data底层会根据一些规则来进行相应的操作。
所以方法的名字是不能随便写的,不然就无法执行想要的操作。

package com.zzh.repository;import com.zzh.domain.Employee;
import org.springframework.data.repository.Repository;public interface EmployeeRepository extends Repository<Employee,Integer>{Employee findByName(String name);
}
  • 创建测试类

findByName方法体中先不用写,直接执行空的测试方法,我们的Employee表就自动被创建了,此时表中没有数据,向里面添加一条数据用于测试:

917807-20170522215047804-355275733.png

package com.zzh.repository;import com.zzh.domain.Employee;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class EmployeeRepositoryTest {private ApplicationContext ctx = null;private EmployeeRepository employeeRepository = null;@Beforepublic void setUp() throws Exception {ctx = new ClassPathXmlApplicationContext("beans-new.xml");employeeRepository = ctx.getBean(EmployeeRepository.class);}@Afterpublic void tearDown() throws Exception {ctx = null;}@Testpublic void findByName() throws Exception {Employee employee = employeeRepository.findByName("zhangsan");System.out.println("id:" + employee.getId() + " name:" + employee.getName() + " age:" + employee.getAge());}}

再执行测试类方法体中的内容:

917807-20170522215237882-678178166.png


Repository接口

  1. Repository接口是Spring Data的核心接口,不提供任何方法

  2. 使用 @ RepositoryDefinition注解跟继承Repository是同样的效果,例如 @ RepositoryDefinition(domainClass = Employee.class, idClass = Integer.class)

  3. Repository接口定义为:public interface Repository<T, ID extends Serializable> {},Repository是一个空接口,标记接口;只要将自定义的接口继承Repository,就会被spring来管理。

Repository的子接口

917807-20170522223916132-1665772406.png

  • CrudRepository :继承 Repository,实现了CRUD相关的方法。

  • PagingAndSortingRepository : 继承 CrudRepository,实现了分页排序相关的方法。

  • JpaRepository :继承 PagingAndSortingRepository ,实现了JPA规范相关的方法。

917807-20170522223904679-681095022.png

Repository中查询方法定义规则

上面一个例子中使用了findByName作为方法名进行指定查询,但是如果把名字改为其他没有规则的比如test就无法获得正确的查询结果。

有如下规则:

917807-20170523091308398-1247277875.png

917807-20170523091651663-106020302.png

最右边是sql语法,中间的就是spring data操作规范,现在写一些小例子来示范一下:

先在employee表中初始化了一些数据:
917807-20170523092341148-870427246.png

在继承了Repository接口的EmployeeRepository接口中新增一个方法:

917807-20170523093450679-2080962887.png

条件是名字以test开头,并且年龄小于22岁,在测试类中进行测试:

917807-20170523093618054-1340669783.png

得到结果:

917807-20170523093746242-2037251949.png

在换一个名字要在某个范围以内并且年龄要小于某个值:

917807-20170523101310663-801837867.png

测试类:

917807-20170523101340742-1274203366.png

得到结果,只有test1和test2,因为在test1,test2和test3里面,年龄还要小于22,所以test3被排除了:
917807-20170523101359585-1970300071.png

弊端分析

对于按照方法名命名规则来使用的弊端在于:

  • 方法名会比较长

  • 对于一些复杂的查询很难实现


Query注解

  • 只需要将 @ Query标记在继承了Repository的自定义接口的方法上,就不再需要遵循查询方法命名规则。

  • 支持命名参数及索引参数的使用

  • 本地查询

案例使用:

  • 查询Id最大的员工信息:
 @Query("select o from Employee o where id=(select max(id) from Employee t1)")Employee getEmployeeById();

注意:Query语句中第一个Employee是类名

测试类:

@Testpublic void getEmployeeByMaxId() throws Exception {Employee employee = employeeRepository.getEmployeeByMaxId();System.out.println("id:" + employee.getId() + " name:" + employee.getName() + " age:" + employee.getAge());}
  • 根据占位符进行查询

注意占位符后从1开始

    @Query("select o from Employee o where o.name=?1 and o.age=?2")List<Employee> queryParams1(String name, Integer age);

测试方法:

@Testpublic void queryParams1() throws Exception {List<Employee> employees = employeeRepository.queryParams1("zhangsan", 20);for (Employee employee : employees) {System.out.println("id:" + employee.getId() + " name:" + employee.getName() + " age:" + employee.getAge());}}
  • 根据命名参数的方式
   @Query("select o from Employee o where o.name=:name and o.age=:age")List<Employee> queryParams2(@Param("name") String name, @Param("age") Integer age);
  • like查询语句
@Query("select o from Employee o where o.name like %?1%")List<Employee> queryLike1(String name);
@Testpublic void queryLike1() throws Exception {List<Employee> employees = employeeRepository.queryLike1("test");for (Employee employee : employees) {System.out.println("id:" + employee.getId() + " name:" + employee.getName() + " age:" + employee.getAge());}}

查询包含test的记录:

917807-20170523141713226-1077587238.png

P.s 这里查询也得到了结果,不过我的idea却在sql语句里面提示有错,我怀疑是我的idea版本的问题,我的是2017.1.3版本的:
917807-20170523142928445-1388456758.png

  • like语句使用命名参数
    @Query("select o from Employee o where o.name like %:name%")List<Employee> queryLike2(@Param("name") String name);

本地查询

所谓本地查询,就是使用原生的sql语句进行查询数据库的操作。但是在Query中原生态查询默认是关闭的,需要手动设置为true:

    @Query(nativeQuery = true, value = "select count(1) from employee")long getCount();

方法是拿到记录数,这里使用的employee是表而不是类,也是原生态查询的特点。


更新操作整合事物使用

  • 在dao中定义方法根据id来更新年龄(Modifying注解代表允许修改)
    @Modifying@Query("update Employee o set o.age = :age where o.id = :id")void update(@Param("id") Integer id, @Param("age") Integer age);

要注意,执行更新或者删除操作是需要事物支持,所以通过service层来增加事物功能,在update方法上添加Transactional注解。

package com.zzh.service;import com.zzh.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class EmployeeService {@Autowiredprivate EmployeeRepository employeeRepository;@Transactionalpublic void update(Integer id, Integer age) {employeeRepository.update(id,age);}
}
  • 删除操作

删除操作同样需要Query注解,Modifying注解和Transactional注解

    @Modifying@Query("delete from Employee o where o.id = :id")void delete(@Param("id") Integer id);
    @Transactionalpublic void delete(Integer id) {employeeRepository.delete(id);}

CrudRepository接口

917807-20170523184038085-29589914.png

  • 创建接口继承CrudRepository
package com.zzh.repository;import com.zzh.domain.Employee;
import org.springframework.data.repository.CrudRepository;public interface EmployeeCrudRepository extends CrudRepository<Employee,Integer>{}
  • 在service层中调用
    @Autowiredprivate EmployeeCrudRepository employeeCrudRepository;

存入多个对象:

    @Transactionalpublic void save(List<Employee> employees) {employeeCrudRepository.save(employees);}

创建测试类,将要插入的100条记录放在List中:

package com.zzh.repository;import com.zzh.domain.Employee;
import com.zzh.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.ArrayList;
import java.util.List;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans-new.xml"})
public class EmployeeCrudRepositoryTest {@Autowiredprivate EmployeeService employeeService;@Testpublic void testSave() {List<Employee> employees = new ArrayList<>();Employee employee = null;for (int i = 0; i < 100; i++) {employee = new Employee();employee.setName("test" + i);employee.setAge(100 - i);employees.add(employee);}employeeService.save(employees);}
}

为了不影响之前的employee表,现在在Employee类上面加上Table注解,指定一个新的表名,之前没有加这个注解默认使用了类名当作表名,也就是employee,现在指定新表test_employee。

917807-20170523192717648-1556336500.png

执行测试类中的方法,控制台输出了很多插入语句,打开数据库查看,新表创建成功,同时插入了100条数据:

917807-20170523193040101-1128212660.png

CrudRepository总结

可以发现在自定义的EmployeeCrudRepository中,只需要声明接口并继承CrudRepository就可以直接使用了。


PagingAndSortingRepository接口

  • 该接口包含分页和排序的功能

  • 带排序的查询:findAll(Sort sort)

  • 带排序的分页查询:findAll(Pageable pageable)

自定义接口

package com.zzh.repository;import com.zzh.domain.Employee;
import org.springframework.data.repository.PagingAndSortingRepository;public interface EmployeePagingAndSortingRepository extends PagingAndSortingRepository<Employee, Integer> {}

测试类

分页:

package com.zzh.repository;import com.zzh.domain.Employee;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans-new.xml"})
public class EmployeePagingAndSortingRepositoryTest {@Autowiredprivate EmployeePagingAndSortingRepository employeePagingAndSortingRepository;@Testpublic void testPage() {//第0页,每页5条记录Pageable pageable = new PageRequest(0, 5);Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable);System.out.println("查询的总页数:"+ page.getTotalPages());System.out.println("总记录数:"+ page.getTotalElements());System.out.println("当前第几页:"+ page.getNumber()+1);System.out.println("当前页面对象的集合:"+ page.getContent());System.out.println("当前页面的记录数:"+ page.getNumberOfElements());}}

917807-20170523202810117-1486459772.png

排序:

在PageRequest的构造函数里还可以传入一个参数Sort,而Sort的构造函数可以传入一个Order,Order可以理解为关系型数据库中的Order;Order的构造函数Direction和property参数代表按照哪个字段进行升序还是降序。

现在按照id进行降序排序:

 @Testpublic void testPageAndSort() {Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id");Sort sort = new Sort(order);Pageable pageable = new PageRequest(0, 5, sort);Page<Employee> page = employeePagingAndSortingRepository.findAll(pageable);System.out.println("查询的总页数:"+ page.getTotalPages());System.out.println("总记录数:"+ page.getTotalElements());System.out.println("当前第几页:" + page.getNumber() + 1);System.out.println("当前页面对象的集合:"+ page.getContent());System.out.println("当前页面的记录数:"+ page.getNumberOfElements());}

917807-20170523205334070-1528758030.png

可以看到id是从100开始降序排列。


JpaRepository接口

917807-20170524080543013-851438576.png

  • 创建接口继承JpaRepository
package com.zzh.repository;import com.zzh.domain.Employee;
import org.springframework.data.jpa.repository.JpaRepository;public interface EmployeeJpaRepository extends JpaRepository<Employee,Integer>{
}
  • 测试类

按id查找员工

package com.zzh.repository;import com.zzh.domain.Employee;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans-new.xml"})
public class EmployeeJpaRepositoryTest {@Autowiredprivate EmployeeJpaRepository employeeJpaRepository;@Testpublic void testFind() {Employee employee = employeeJpaRepository.findOne(99);System.out.println(employee);}
}

查看员工是否存在

@Testpublic void testExists() {Boolean result1 = employeeJpaRepository.exists(25);Boolean result2 = employeeJpaRepository.exists(130);System.out.println("Employee-25: " + result1);System.out.println("Employee-130: " + result2);}

JpaSpecificationExecutor接口

  • Specification封装了JPA Criteria查询条件

  • 没有继承其他接口。

自定义接口

这里要尤为注意,为什么我除了继承JpaSpecificationExecutor还要继承JpaRepository,就像前面说的,JpaSpecificationExecutor没有继承任何接口,如果我不继承JpaRepository,那也就意味着不能继承Repository接口,spring就不能进行管理,后面的自定义接口注入就无法完成。

package com.zzh.repository;import com.zzh.domain.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;public interface EmployeeJpaSpecificationExecutor extends JpaSpecificationExecutor<Employee>,JpaRepository<Employee,Integer> {
}

测试类

测试结果包含分页,降序排序,查询条件为年龄大于50

package com.zzh.repository;import com.zzh.domain.Employee;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.persistence.criteria.*;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans-new.xml"})
public class EmployeeJpaSpecificationExecutorTest {@Autowiredprivate EmployeeJpaSpecificationExecutor employeeJpaSpecificationExecutor;/*** 分页* 排序* 查询条件: age > 50*/@Testpublic void testQuery() {Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id");Sort sort = new Sort(order);Pageable pageable = new PageRequest(0, 5, sort);/*** root:查询的类型(Employee)* query:添加查询条件* cb:构建Predicate*/Specification<Employee> specification = new Specification<Employee>() {@Overridepublic Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {Path path = root.get("age");return cb.gt(path, 50);}};Page<Employee> page = employeeJpaSpecificationExecutor.findAll(specification, pageable);System.out.println("查询的总页数:"+ page.getTotalPages());System.out.println("总记录数:"+ page.getTotalElements());System.out.println("当前第几页:" + page.getNumber() + 1);System.out.println("当前页面对象的集合:"+ page.getContent());System.out.println("当前页面的记录数:"+ page.getNumberOfElements());}}

可以看到id是从50开始,而不是100,因为设定了查询条件age>50

917807-20170524092721794-955880776.png


总结

本篇文章首先介绍以原始的jdbc方式和Spring JdbcTemplate进行数据库相关操作,接着讲解了Spring Data Jpa的Repository接口和增删查改有关的注解,最后介绍了Repository的一些子接口相关的crud,分页和排序。

GitHub地址:SpringDataJpa

转载于:https://www.cnblogs.com/zhaozihan/p/6884077.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/392697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

azure多功能成像好用吗_Azure持久功能简介:模式和最佳实践

azure多功能成像好用吗Authored with Steef-Jan Wiggers at Microsoft Azure由Microsoft Azure的Steef-Jan Wiggers撰写 With Durable Functions, you can program a workflow and instantiate tasks in sequential or parallel order, or you can build a watch or support a…

leetcode 327. 区间和的个数(treemap)

给定一个整数数组 nums&#xff0c;返回区间和在 [lower, upper] 之间的个数&#xff0c;包含 lower 和 upper。 区间和 S(i, j) 表示在 nums 中&#xff0c;位置从 i 到 j 的元素之和&#xff0c;包含 i 和 j (i ≤ j)。 说明: 最直观的算法复杂度是 O(n2) &#xff0c;请在此…

常用的工具函数

得到两个数组的并集, 两个数组的元素为数值或字符串//tools.js export const getUnion (arr1, arr2) > {return Array.from(new Set([...arr1, ...arr2])) }//调用页面 import { getUnion } from /libs/toolsthis.getUnion getUnion([1,2,3,5],[1,4,6]) //(6) [1, 2, 3,…

git 常用commands(转)

常用 Git 命令清单 作者&#xff1a; 阮一峰 日期&#xff1a; 2015年12月 9日 我每天使用 Git &#xff0c;但是很多命令记不住。 一般来说&#xff0c;日常使用只要记住下图6个命令&#xff0c;就可以了。但是熟练使用&#xff0c;恐怕要记住60&#xff5e;100个命令。 下面是…

Win2003磁盘分区调整

引用如下&#xff1a; 可能大家都知道&#xff0c;在Windows Server 2003下&#xff0c;普通版本的分区魔术师是无法运行的&#xff0c;而Windows内置的命令行工具Diskpart则能胜任分区魔术师的大部分工作&#xff0c;它的功能非常强大。输入Diskpart后&#xff0c;将显示如图所…

检查集群状态命令_轻松管理Kubernetes集群的7个工具

Kubernetes正在不断加快在云原生环境的应用&#xff0c;但如何以统一、安全的方式对运行于任何地方的Kubernetes集群进行管理面临着挑战&#xff0c;而有效的管理工具能够大大降低管理的难度。K9sk9s是基于终端的资源仪表板。它只有一个命令行界面。无论在Kubernetes仪表板Web …

leetcode 122. 买卖股票的最佳时机 II(贪心算法)

给定一个数组&#xff0c;它的第 i 个元素是一支给定股票第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;。 注意&#xff1a;你不能同时参与多笔交易&#xff08;你必须在再次购买前出售掉…

前端绘制绘制图表_绘制图表(第2页):JavaScript图表库的比较

前端绘制绘制图表by Mandi Cai蔡曼迪 绘制图表(第2页)&#xff1a;JavaScript图表库的比较 (Charting the waters (pt. 2): a comparison of JavaScript charting libraries) 深入研究D3.js&#xff0c;Dygraphs&#xff0c;Chart.js和Google Charts (A deep dive into D3.js,…

python 3.6.5 pip_在Windows 10 + Python 3.6.5 中用 pip 安装最新版 TensorFlow v1.8 for GPU

声明什么cuDNN之类的安装&#xff0c;应该是毫无难度的&#xff0c;按照官网的教程来即可&#xff0c;除非。。。像我一样踩了狗屎运。咳咳&#xff0c;这些问题不是本文的关键。本文的关键是解决pip安装tensorflow gpu版的问题。安装环境操作系统&#xff1a;64位的Windows 10…

模板进阶——模板实参推断

一、关键点 模板实参&#xff1a;模板参数T的实例类型&#xff0c;如int、string等 模板实参推断&#xff1a;从函数实参来确定模板实参的过程 模板类型参数与类型转换&#xff1a;const的转换、数组/函数到指针的转换 显式模板实参&#xff1a;当模板参数类型并未出现在函数参…

leetcode 973. 最接近原点的 K 个点(排序)

我们有一个由平面上的点组成的列表 points。需要从中找出 K 个距离原点 (0, 0) 最近的点。 &#xff08;这里&#xff0c;平面上两点之间的距离是欧几里德距离。&#xff09; 你可以按任何顺序返回答案。除了点坐标的顺序之外&#xff0c;答案确保是唯一的。 示例 1&#xf…

ios 打开扬声器

[[UIDevice currentDevice] setProximityMonitoringEnabled:YES]; AVAudioSession *audioSession [AVAudioSession sharedInstance]; //默认情况下扬声器播放 [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthe…

sqlserver 批量处理数据

目前我觉得有两种方法可以用作批量数据的处理&#xff0c;也算比较靠谱的吧&#xff1a;sqlbulkcopy 和利用表值函数。 1.sqlbulkcopy是dotnet中的一个用来处理大批量插入数据的&#xff0c;具体用法如下&#xff1a; using (SqlConnection conSave new SqlConnection(Config.…

区块链编程语言_区块链开发中使用的最受欢迎的编程语言

区块链编程语言by Michael Draper通过迈克尔德雷珀(Michael Draper) We’re currently in the midst of a new burgeoning industry with blockchain development.我们目前正处于区块链开发的新兴行业中。 Blockchain technology is very much in a nascent stage, however t…

vscode 模糊部分代码_本周 GitHub 速览:您的代码有声儿吗?(Vol.38)

作者&#xff1a;HelloGitHub-小鱼干摘要&#xff1a;还记得花式夸赞程序员的彩虹屁插件 vscode-rainbow-fart 吗&#xff1f;它后续有人啦&#xff01;JazzIt 同它的前辈 vscode-rainbow-fart 一样&#xff0c;是一个能让代码“发声”的工具&#xff0c;它会在脚本运行成功或者…

有趣的链接

1行命令实现人脸识别&#xff1a;https://linux.cn/article-9003-1.html转载于:https://blog.51cto.com/10704527/1983007

webpack基础使用Loader(三)

loaders:[ { test:/\.js$/, loader:babel-loader, exclude:__dirname"/node_modules/", //排除打包的范围&#xff08;需要绝对路径&#xff09; include:__dirname"src",//指定打包的范围&#xff08;需要绝对路径&#xff09; query:{ …

Flutter VS React Native –为什么我认为Flutter最适合移动应用程序开发

This isn’t the type of article you might think it’s going to be. I’m not going to list the pros and cons of every framework and I am not going to do a comparative analysis of performance. 这不是您可能会想到的文章类型。 我不会列出每个框架的优缺点&#xf…

python 2.7 error: Microsoft Visual C++ 9.0 is required

参考&#xff1a;https://stackoverflow.com/questions/43645519/microsoft-visual-c-9-0-is-required 解决方法&#xff1a; 下载并安装Microsoft Visual C Compiler for Python 2.7&#xff1a; Microsoft Visual C Compiler for Python 2.7 转载于:https://www.cnblogs.com/…

python内置支持集合运算吗_Python中的集合支持交、并运算

Python中的集合支持交、并运算答&#xff1a;√新冠肺炎患者潜伏期的传染性最强答&#xff1a;对在运动的组接中&#xff0c;镜头组接一个基本的原则是()、()。答&#xff1a;动接动 静接静在中指背,距指甲根中点1分许称答&#xff1a;老龙库存控制属于生产管理而不是物流管理的…