1. 程式人生 > >04 SpringBoot對資料庫的支援

04 SpringBoot對資料庫的支援

文章目錄

簡介

Spring Data專案是Spring用來解決資料訪問問題的一攬子解決方案

Spring Data提供了統一的API,對資料儲存技術進行支援

不同的持久層技術,有不同的Spring Data子專案

這些子專案都通過Spring Data Commons專案來實現

Spring DataCommons讓我們在使用關係型或非關係型資料庫技術時,都使用基於Spring的統一標準

包含:CRUD、查詢、排序、分頁

常用子專案

  • Spring Data JPA
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</
artifactId
>
<version>1.11.10.RELEASE</version> </dependency>
  • Spring Data MongoDB
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-mongodb -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>1.10.10.RELEASE</version>
</dependency>

  • Spring Data Neo4J
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-neo4j -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-neo4j</artifactId>
    <version>5.0.3.RELEASE</version>
</dependency>

  • Spring Data Redis
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

  • Spring Data Solr
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-solr -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-solr</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>

  • Spring For Apache Hadoop
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-hadoop -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-hadoop</artifactId>
    <version>2.5.0.RELEASE</version>
</dependency>

  • Spring Data GemFire
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-gemfire -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-gemfire</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

  • Spring Data REST WebMVC
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-rest-webmvc -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-rest-webmvc</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>

  • Spring Data Couchbase
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-couchbase -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-couchbase</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>

  • Spring Data Elasticsearch
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-elasticsearch -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-elasticsearch</artifactId>
    <version>3.0.3.RELEASE</version>
</dependency>

  • Spring Data For Apache Cassandra Core
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-cassandra -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-cassandra</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

Repository介面

package org.springframework.data.repository;

import java.io.Serializable;
public interface Repository<T, ID extends Serializable> {

}

Repository根介面接收JPA領域類和領域類的id型別作為型別引數

CrudRepository

CrudRepository子介面定義了和CRUD相關操作的內容


package org.springframework.data.repository;

import java.io.Serializable;


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


	<S extends T> S save(S entity);


	<S extends T> Iterable<S> save(Iterable<S> entities);


	T findOne(ID id);

	boolean exists(ID id);

	Iterable<T> findAll();


	Iterable<T> findAll(Iterable<ID> ids);

	long count();


	void delete(ID id);


	void delete(T entity);


	void delete(Iterable<? extends T> entities);


	void deleteAll();
}

PagingAndSortingRepository

PagingAndSortingRepository子介面定義了分頁、排序相關的內容


package org.springframework.data.repository;

import java.io.Serializable;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

/**
 * Extension of {@link CrudRepository} to provide additional methods to retrieve entities using the pagination and
 * sorting abstraction.
 * 
 * @author Oliver Gierke
 * @see Sort
 * @see Pageable
 * @see Page
 */
@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);
}

小結

不同的資料訪問技術提供了不同的Repository

如:Spring Data JPA有JpaRepository;Spring Data MongoDB有MongoRepository

Spring Data專案有一個激動人心的功能,可以根據屬性名進行計數、刪除、查詢等方法的操作

關鍵字 方法命名 sql where字句
And findByNameAndPwd where name= ? and pwd =?
Or findByNameOrSex where name= ? or sex=?
Is,Equals findById,findByIdEquals where id= ?
Between findByIdBetween where id between ? and ?
LessThan findByIdLessThan where id < ?
LessThanEquals findByIdLessThanEquals where id <= ?
GreaterThan findByIdGreaterThan where id > ?
GreaterThanEquals findByIdGreaterThanEquals where id > = ?
After findByIdAfter where id > ?
Before findByIdBefore where id < ?
IsNull findByNameIsNull where name is null
isNotNull,NotNull findByNameNotNull where name is not null
Like findByNameLike where name like ?
NotLike findByNameNotLike where name not like ?
StartingWith findByNameStartingWith where name like ‘?%’
EndingWith findByNameEndingWith where name like ‘%?’
Containing findByNameContaining where name like ‘%?%’
OrderBy findByIdOrderByXDesc where id=? order by x desc
Not findByNameNot where name <> ?
In findByIdIn(Collection<?> c) where id in (?)
NotIn findByIdNotIn(Collection<?> c) where id not in (?)
True findByAaaTue where aaa = true
False findByAaaFalse where aaa = false
IgnoreCase findByNameIgnoreCase where UPPER(name)=UPPER(?

find方法是查詢,如果是計數,則為count,刪除,則為delete

限制結果數量:top和first關鍵字,如,findFirst10ByName,findTop10ByName

Spring Data JPA

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
  • 資料來源
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/tdk?useSSL=false&useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  jackson:
    serialization: true

自動配置

SpringBoot自動開啟了註解事務的支援(@EnableTransactionManagement

還配置了一個JdbcTemplate

Spring還提供了一個初始化資料的功能,放在類路徑下的schema.sql自動初始化表結構,data.sql自動填充表資料

SpringData對JPA的自動配置放在org.springframework.boot.autoconfigure.orm.jpa

預設的JPA實現是Hibernate

配置JPA的字首為:spring.jpa

自動掃描註解有@Entity的實體類

在Web專案中,我們經常遇到控制器或頁面訪問資料的時候出現會話連線關閉的錯誤

這個時候,我們會配置一個Open EntityManager(Session)In View這個過濾器

SpringBoot為我們自動配置了OpenEntityManagerInViewInterceptor這個Bean,

並且已經註冊到SpringMVC攔截器。

無需手動開啟

@EnableJpaRepositories

案例

  • pom

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
  • domain
package org.zln.spb.demo01.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

/**
 * @Author 張柳寧
 * @Description
 * @Date Create in 2018/1/31
 * @Modified By:
 */
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@NamedQuery(name = "Person.withNameAndAddressNamedQuery",
        query = "select p from Person p where p.name = ?1 and p.address = ?2")
public class Person {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private Integer age;
    private String address;
}

  • dao
package org.zln.spb.demo01.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.zln.spb.demo01.domain.Person;

import java.util.List;

/**
 * @Author 張柳寧
 * @Description
 * @Date Create in 2018/1/31
 * @Modified By:
 */

public interface PersonDao extends JpaRepository<Person,Long> {

    List<Person> findByAddress(String address);

    Person findByNameAndAddress(String name,String address);

    @Query("select p from Person p where p.name = :name and p.address = :address")
    Person withNameAndAddressQuery(@Param("name") String name,@Param("address") String address);

    Person withNameAndAddressNamedQuery(String name,String address);

    List<Person> findByNameLike(String name);



}


  • 測試
package org.zln.spb.demo01.dao;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zln.spb.demo01.domain.Person;

import java.util.List;

import static org.junit.Assert.*;

/**
 * @Author 張柳寧
 * @Description
 * @Date Create in 2018/1/31
 * @Modified By:
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonDaoTest {

    @Autowired
    private PersonDao personDao;

    @Test
    public void findByAddress() {
        personDao.findByAddress("地址");
    }

    @Test
    public void findByNameAndAddress() {
        personDao.findByNameAndAddress("姓名","地址");
    }

    @Test
    public void withNameAndAddressQuery() {
        personDao.withNameAndAddressQuery("姓名","地址");
    }

    @Test
    public void withNameAndAddressNamedQuery() {
        personDao.withNameAndAddressNamedQuery("姓名","地址");
    }

    @Test
    public void findByNameLike() {
        List<Person> personList = personDao.findByNameLike("%name%");
        System.out.println(personList);
    }
}
  • 排序
personList = personDao.findAll(new Sort(Sort.Direction.DESC,"age"));
  • 分頁查詢
Page<Person> people = personDao.findAll(new PageRequest(2,3));
for (Person person:people){
  System.out.println(person);
}

小結

對於大多數的SQL,其實都是非常簡單的單表查詢

這個時候使用IDEA對Hibernate的自動生成程式碼,非常方便的

對於一些複雜SQL,再考慮使用MyBatis或者JdbcTemplate

整合H2資料庫

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>

server:
  port: 8000
spring:
  jpa:
    generate-ddl: false
    show-sql: true
    hibernate:
      ddl-auto: none
  datasource:                           # 指定資料來源
    platform: h2                        # 指定資料來源型別
    schema: classpath:schema.sql        # 指定h2資料庫的建表指令碼
    data: classpath:data.sql            # 指定h2資料庫的資料指令碼
logging:                                # 配置日誌級別,讓hibernate打印出執行的SQL
  level:
    root: INFO
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE

HikariDataSource資料來源

配置

# jdbc_config   datasource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/datebook?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
spring.datasource.username=root
spring.datasource.password=root
# Hikari will use the above plus the following to setup connection pooling
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1
spring:
  profiles:
    active: prod
  datasource:
  #  使用高效能資料來源
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.jdbc.Driver
    hikari:
      minimum-idle: 15
      maximum-pool-size: 50
      idle-timeout: 30000
      pool-name: DatebookHikariCP
      maxLifetime: 40000 # 這個值要配置的比mysql的wait_timeout小,預設值是30分鐘。但是不得配置少於30秒,佛足額就會重置回預設值
      connection-timeout: 30000
      connection-test-query: SELECT 1
      connection-init-sql: set names utf8mb4
      validation-timeout: 5000

springboot 2.0 預設連線池就是Hikari了,所以引用parents後不用專門加依賴

java配置

Java配置資料來源

    public HikariDataSource dataSourceConf(MutiDataSourceProperties dataSource) {
        //資料來源配置資訊
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(dataSource.getUrl());
        hikariConfig.setUsername(dataSource.getUsername());
        hikariConfig.setPassword(dataSource.getPassword());
        hikariConfi