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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章