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();
}