SpringData-JPA
SpringData-JPA
SpringData可以结合JPA使用也可以结合JDBC使用
一、SpringData之后HelloWorld
新建一个maven工程
1.pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springdata.jpa</groupId>
<artifactId>springdata_jpa</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.3.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.3.3.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.common</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>5.0.1.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta2</version>
</dependency>
</dependencies>
</project>
2.属性配置文件db.properties
jdbc.user=root
jdbc.password=mysql123
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///jpa?characterEncoding=utf8
3.Spring配置文件application.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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--扫描spring 组件-->
<context:component-scan base-package="com.springdata.jpa"></context:component-scan>
<!--加载属性配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--配置数据源-->
<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>
<!--配置EntityManagerFactory-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--配置实体类存放路径-->
<property name="packagesToScan" value="com.springdata.jpa"></property>
<!--配置jpa实现产品-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
</property>
<!--配置jpa属性-->
<property name="jpaProperties">
<props>
<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"></tx:annotation-driven>
<!--配置spring-data-->
<jpa:repositories base-package="com.springdata.jpa"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
</beans>
如果报错:EntityPathResolver must not be null!
要配置 包扫描,使扫描到repository
<!--扫描spring 组件-->
<context:component-scan base-package="com.springdata.jpa"></context:component-scan>
4.Entity实体
package com.springdata.jpa.entities;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Entity
@Table(name = "tb_cat")
public class Cat implements Serializable {
@Id
@GeneratedValue
private Integer id;
@Column(name = "cat_name")
private String catName;
@Column(name = "date")
@Temporal(TemporalType.DATE)
private Date date;
// 省略了getter/setter和toString
}
5.Repositoty类
// 不需要手动配置加载到Spring容器中去
// 继承Repository,第一个参数是对应的实体类,第二个参数是对应的主键类型
public interface CatRepository extends Repository<Cat, Integer> {
// getBy后sin 可以接对象的属性
Cat getByCatName(String name);
}
6.测试
注:测试前,在数据库中手动添加一条数据。
@Test
public void testHelloWorld() {
ApplicationContext act = new ClassPathXmlApplicationContext("application.xml");
CatRepository bean = act.getBean(CatRepository.class);
Cat aa = bean.getByCatName("aa");
System.out.println(aa);
}
控制台打印:
Hibernate:
select
cat0_.id as id1_0_,
cat0_.cat_name as cat_name2_0_,
cat0_.date as date3_0_
from
tb_cat cat0_
where
cat0_.cat_name=?
Cat{id=1, catName='aa', date=2018-10-03}
二、Repository接口
1.继承Repository接口
// 第一个参数是实体对象类,第二个参数是对象的主键类型
public interface DogDao extends Repository<Dog, Integer>{
Dog getByDogName(String dogName);
}
2.@RepositoryDefinition注解
// 第一个属性domainClass的值是实体类对象类,第二个属性的对象的主键类型
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
Dog getByDogName(String dogName);
}
3.Repository中方法的定义
- 查询
1.查询方法要以get,find,read开头
2.方法名的定义要依据规范
3.查询条件可以用And或Or连接
3.查询字段用对象实体类属性名代替
4.具体命名规则如下:
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstname ,findByFirstnameIs ,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended % ) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended % ) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in % ) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
Dog getByDogName(String dogName);
List<Dog> getByDogNameLikeAndIdLessThan(String like, Integer id);
List<Dog> findByDogNameStartingWithAndIdGreaterThan(String start, Integer id);
List<Dog> readByDogNameLikeOrId(String name, Integer id);
}
4.@Query()注解使用JPQL
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
Dog getByDogName(String dogName);
List<Dog> getByDogNameLikeAndIdLessThan(String like, Integer id);
List<Dog> findByDogNameStartingWithAndIdGreaterThan(String start, Integer id);
List<Dog> readByDogNameLikeOrId(String name, Integer id);
// 使用@Query注解可以自定义jpql语句
@Query("SELECT d1 FROM Dog d1 WHERE d1.id =(SELECT MAX(d2.id) FROM Dog d2)")
Dog selectDogByMaxId(); // 方法命名可以不用spring data jpa提供的命名规范了
}
5.@Query传参
1.占位符顺序传参
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
// 使用@Query注解可以自定义jpql语句
@Query("SELECT d1 FROM Dog d1 WHERE d1.id =(SELECT MAX(d2.id) FROM Dog d2)")
Dog selectDogByMaxId();
// 写占位符?的时候,一定要指定数字,这个数字代表这个占位符接收的是方法中对应的第几个参数
@Query("SELECT d FROM Dog d WHERE d.id = ?2 and d.dogName = ?1")
List<Dog> selectDogByDogNameAndId(String dogName, Integer id);
}
2.命名参数传参
@RepositoryDefinition(domainClass = Dog.class, idClass = Integer.class)
public interface DogDao {
// 使用@Query注解可以自定义jpql语句
@Query("SELECT d1 FROM Dog d1 WHERE d1.id =(SELECT MAX(d2.id) FROM Dog d2)")
Dog selectDogByMaxId();
@Query("SELECT d FROM Dog d WHERE d.id = ?2 and d.dogName = ?1")
List<Dog> selectDogByDogNameAndId(String dogName, Integer id);
// 使用 :参数名的方式,指定要接收的参数的命名,在方法的参数列表位置通过@Param参数指定对应的参数的命名,
// 当两个参数命名一致时,才能写入值
@Query("SELECT d FROM Dog d WHERE d.id = :id and d.dogName = :dogName")
List<Dog> selectDogsByNameAndId(@Param("dogName") String name, @Param("id")Integer id);
}
3.关于模糊查询
方式一:
// 这个方法进行模糊查询的时候,要传入%号
@Query("FROM Dog d WHERE d.dogName like ?1")
public List<Dog> getDogByNameLike(String like);
// 方法的使用,传入的参数要带上%
List<Dog> dogs = dao.getDogByNameLike("%a%");
方式二:
// 这里%也可以写在参数位置
@Query("FROM Dog d WHERE d.dogName like %:name")
public List<Dog> getDogsByNameLike(@Param("name") String dogName);
// 方法的使用,参数位置可以不传入%
List<Dog> dogs = dao.getDogsByNameLike("a");
6.@Query注解写SQL查询
// 在@Query注解中设置nativeQuery属性为true
@Query(value = "SELECT COUNT(1) FROM tb_dog", nativeQuery = true)
Integer getCount();
// 参数可以写成占位符的形式,方法中传参,依据占位符的顺序
@Query(value = "select * from tb_dog where id = ? and dog_name = ?", nativeQuery = true)
Dog getDogById(Integer id, String name);
7.@Modifying修改和删除
修改:
@RepositoryDefinition(domainClass = Person.class, idClass = Integer.class)
public interface PersonDao {
@Query("FROM Person p WHERE p.id = ?1")
Person getPersonById(Integer id);
@Query(value = "SELECT * FROM tb_person WHERE last_name = ?", nativeQuery = true)
Person getPersonByLastName(String lastName);
// 使用修改和删除时,使用@Modifying+@Query注解
@Modifying
@Query("UPDATE Person p SET p.lastName = :lastName WHERE p.id = :id")
int updateLastNameById(@Param("lastName")String lastName, @Param("id")Integer id);
}
如果直接调用这个Repository中的方法会报错!
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
修改和删除操作时,必须要有事务!
所以修改代码,新建一个PersonService类,在PersonService中使用PersonDao方法,并添加事务!
@Service // 加入到Spring容器中,注意配置包扫描
public class PersonService {
private PersonDao personDao;
// 在构造器位置,personDao可以自动注入,类似于@Autowired
public PersonService(PersonDao personDao) {
this.personDao = personDao;
}
@Transactional // 添加事务支持
public void updatePerson(String lastName, Integer id) {
int i = personDao.updateLastNameById(lastName, id);
System.out.println(i);
}
}
测试:
@Test
public void testUpdate() {
PersonService personService = act.getBean(PersonService.class);
personService.updatePerson("小明", 2);
}
删除:
@RepositoryDefinition(domainClass = Person.class, idClass = Integer.class)
public interface PersonDao {
@Query("FROM Person p WHERE p.id = ?1")
Person getPersonById(Integer id);
@Query(value = "SELECT * FROM tb_person WHERE last_name = ?", nativeQuery = true)
Person getPersonByLastName(String lastName);
@Modifying
@Query("UPDATE Person p SET p.lastName = :lastName WHERE p.id = :id")
int updateLastNameById(@Param("lastName")String lastName, @Param("id")Integer id);
// 使用@Modifying和@Query注解完成删除操作
@Modifying
@Query(value = "DELETE FROM tb_person WHERE id = ?", nativeQuery = true)
int deleteById(Integer id);
}
service中使用添加事务支持:
@Service // 加入到Spring容器中,注意配置包扫描
public class PersonService {
private PersonDao personDao;
// 在构造器位置,personDao可以自动注入,类似于@Autowired
public PersonService(PersonDao personDao) {
this.personDao = personDao;
}
@Transactional // 添加事务支持
public void updatePerson(String lastName, Integer id) {
int i = personDao.updateLastNameById(lastName, id);
System.out.println(i);
}
@Transactional // 添加事务支持
public void deletePerson(Integer id) {
int i = personDao.deleteById(id);
System.out.println(i);
}
}
测试:
@Test
public void testDelete() {
PersonService personService = act.getBean(PersonService.class);
personService.deletePerson(1);
}
三、CrudRepository
CrudRepository接口中定义了一些能用的增删改查方法
// 继承CrudRepository
public interface PersonRepository extends CrudRepository<Person, Integer>{
}
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity); //保存单个对象方法
<S extends T> Iterable<S> saveAll(Iterable<S> entities); //批量保存方法
Optional<T> findById(ID id); // 查找方法
boolean existsById(ID id); // 判断是否存在
Iterable<T> findAll(); // 查找所有
Iterable<T> findAllById(Iterable<ID> ids); // 通过id查找所有
long count(); // 获取数据条数
void deleteById(ID id); // 通过id删除
void delete(T entity); // 通过实体对象删除
void deleteAll(Iterable<? ext ends T> entities); // 批量删除
void deleteAll(); // 删除所有
}
测试:
@Test
public void findAllById() {
PersonRepository personRepository = act.getBean(PersonRepository.class);
// findAllById()
Iterable<Person> res = personRepository.findAllById(Arrays.asList(1,2,3,4,5));
System.out.println(res);
}
四、PagingAndSortingRepository接口
// 继承可实现分页和排序
public interface PersonPagingAndSortingRepository extends PagingAndSortingRepository<Person, Integer> {
}
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
// 排序
Iterable<T> findAll(Sort sort);
// 分页
Page<T> findAll(Pageable pageable);
}
使用方法:
分页:
@Test
public void testPageAndSort() {
PersonPagingAndSortingRepository dao = act.getBean(PersonPagingAndSortingRepository.class);
PageRequest pageable = new PageRequest(0, 2);
Page<Person> page = dao.findAll(pageable);
System.out.println(page.getNumber()); // 当前页 页码从0开始计算的
System.out.println(page.getNumberOfElements()); // 总页数
System.out.println(page.getSize()); // 每页大小
System.out.println(page.getContent()); // 每页内容
}
五.JpaRepository
// 接口继承JpaRepository
public interface CustomerDao extends JpaRepository<Customer, Integer> {
}
// JpaRepository是PagingAndSortingRepository,
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
测试:
@Test
public void test() {
Customer customer = new Customer();
customer.setLastName("小芳");
customer.setBirthDay(new Date());
customer.setId(1);
// saveAndFlush()方法,存在就更新,不存在就插入
customerDao.saveAndFlush(customer);
}
六、带条件的分页JpaSpecificationExecutor
public interface CustomerDao extends JpaRepository<Customer, Integer>, JpaSpecificationExecutor<Customer> {
}
使用:
@Test
public void test02() {
Specification<Customer> specification = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
// 构造比较查询条件
Path path = root.get("id");
Predicate predicate = criteriaBuilder.gt(path, 0);
return predicate;
}
};
List<Customer> all = customerDao.findAll(specification);
System.out.println(all);
}
@Test
public void test02() {
Specification<Customer> specification = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Path<String> lastName = root.get("lastName");
Predicate predicate = criteriaBuilder.like(lastName, "%小");
Path<Integer> id = root.get("id");
Predicate predicate1 = criteriaBuilder.equal(id, 2);
// 两个条件and连接
query.where(predicate, predicate1);
return null;
}
};
List<Customer> all = customerDao.findAll(specification);
System.out.println(all);
}
七、自定义Repository
- 1.编写接口,继承Spring Data提供的Repository接口
public interface CustomerDao extends JpaRepository<Customer, Integer>,
JpaSpecificationExecutor<Customer> {
}
- 2.自定义接口,编写要实现的方法
public interface CustomerRepository {
// 自已在接口中定义的方法
public void test();
}
- 3.编写步骤2实现类,实现类的命名必须是步骤1的接口名+Impl
// 继承自定义接口
public class CustomerDaoImpl implements CustomerRepository {
// 可以使用EntityManager
@PersistenceContext
private EntityManager entityManager;
// 实现方法
public void test() {
Customer customer = entityManager.find(Customer.class, 2);
System.out.println(customer);
}
}
- 4.修改步骤1接口,使继承自定义接口
public interface CustomerDao extends JpaRepository<Customer, Integer>,
JpaSpecificationExecutor<Customer>,
//继承自定义接口
CustomerRepository {
}
测试:
@Test
public void repository() {
customerDao.test();
}