SpringDataJPA使用

1、簡介

  SpringDataJpa是 JPA規範的一個很好的實現,簡化了開發的複雜度,極大提高了開發的效率。SpringDataJpa經過 Repository接口及子接口能夠很方便的實現持久化操做。java

  SpringDataJpa的API在線手冊:https://docs.spring.io/spring-data/jpa/docs/2.1.0.RELEASE/api/mysql

  簡單的看下 Repository 類層次結構圖關係:web

  

 

 

   Repository:空的父接口,便是一個標記接口。spring

   CrudRepository:Repository的子接口,提供CRUD操做sql

   PagingAndSortingRepository:CrudRepository的子接口,增長 分頁 和 排序的功能json

  JpaRepository:PagingAndSortingRepository、QueryByExampleExecutor的子接口,增長 批量操做 以及 QBE查詢api

  SimpleJpaRepository:實現了JpaRepository 和 JpaSpecificationExecutor 接口,構造函數中傳入 EntityManager。類上註解了@Transactional(readOnly = true),對於CUD的方法使用了註解@Transactional,局部有限原則。各類方法最終的實現都依託於 EntityManager來完成。springboot

2、使用

  一、建立springboot項目 springdata-jpaapp

  二、src/main/resources 目錄下新建 application.properties,內容以下:dom

db.driverClass=com.mysql.jdbc.Driver
db.jdbcUrl=jdbc:mysql://localhost:3310/boot?useUnicode=true&characterEncoding=UTF-8&useSSL=false
db.username=root
db.password=123456
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
#修改實體類的匹配命名規則,主要影響@column(name="")配置,默認是駝峯匹配法
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

  三、建立 DataSource configuration:

package com.cfang.confingurations;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.druid.pool.DruidDataSource;

@Configuration
public class DataSourceConfigurations {

    @Value("${db.driverClass:com.mysql.jdbc.Driver}")
    private String driverClass;
    @Value("${db.jdbcUrl}")
    private String jdbcUrl;
    @Value("${db.username}")
    private String username;
    @Value("${db.password}")
    private String password;
    @Value("${initialSize:10}")
    private Integer initialSize;    //初始化鏈接池個數
    @Value("${maxActive:20}")
    private Integer maxActive;    //最大鏈接數 
    @Value("${maxWait:60000}")
    private Long maxWait;    //獲取鏈接等待超時時間,單位毫秒
    @Value("${timeBetweenEvictionRunsMillis:60000}")
    private Long timeBetweenEvictionRunsMillis;    //間隔多久進行一次檢測,檢測回收須要關閉的空閒連接,單位毫秒
    @Value("${minEvictableIdleTimeMillis:300000}")
    private Long minEvictableIdleTimeMillis;    //定義一個鏈接在池中的最小生存時間,單位毫秒
    @Value("${validationQuery:select 1 from dual}")
    private String validationQuery;    //用來檢測鏈接是否有效的sql
    @Value("${testWhileIdle:true}")
    private Boolean testWhileIdle;    //申請鏈接的時候是否檢測。若是空間時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測鏈接是否有效
    @Value("${testOnBorrow:false}")
    private Boolean testOnBorrow;    
    @Value("${testOnReturn:false}")
    private Boolean testOnReturn;    //歸還鏈接的時候,是否validationQuery檢查鏈接的有效性,true執行的話,下降性能
    @Value("${poolPreparedStatements:false}")
    private Boolean poolPreparedStatements;    //是否打開pscache
    @Value("${maxPoolPreparedStatementPerConnectionSize:20}")
    private int maxPoolPreparedStatementPerConnectionSize;    //指定每一個鏈接的pscache的大小
    
    @Bean
    public DataSource initDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(jdbcUrl);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setInitialSize(initialSize);
        dataSource.setMaxActive(maxActive);
        dataSource.setMaxWait(maxWait);
        dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        dataSource.setValidationQuery(validationQuery);
        dataSource.setTestWhileIdle(testWhileIdle);
        dataSource.setTestOnBorrow(testOnBorrow);
        dataSource.setTestOnReturn(testOnReturn);
        dataSource.setPoolPreparedStatements(poolPreparedStatements);
        if(poolPreparedStatements) {
            dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        }
        return dataSource;
    }
}

  四、建立User實體

package com.cfang.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Table(name = "tb_user")
@Data
public class TblUserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @Column(name = "name")
    private String name;
    @Column(name = "password")
    private String password;
    @Column(name = "age")
    private int age;
}

  五、建立持久化操做Repository:

package com.cfang.repository;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.cfang.entity.TblUserEntity;

public interface UserRepository extends JpaRepository<TblUserEntity, Integer>, JpaSpecificationExecutor<TblUserEntity>{

    /**
     *     springdata關鍵字命名方法,可不寫實現
     */
    TblUserEntity findByName(String name);
    TblUserEntity findByNameAndAge(String name, int age);
    List<TblUserEntity> findByNameLikeAndAge(String name, int age);
    
    Page<TblUserEntity> findUserPageByNameLike(String name, Pageable pageable);
    
    /**
     * JPQL
     */
    @Query(value = "from TblUserEntity where id=?1", nativeQuery = false)
    TblUserEntity readIdByJPQL(int id);
    
    @Query(value = "select * from tb_user where id=:id", nativeQuery = true)
    TblUserEntity readIdBySQL(int id);
    
    @Modifying
    @Query(value = "update TblUserEntity set name=?2 where id=?1", nativeQuery = false)
    int updateUserNameByIdJPQL(int id, String name);
    
    @Modifying
    @Query(value = "update tb_user set name=:name where id=:id", nativeQuery = true)
    int updateUserNameByIdSQL(@Param("id")int id, @Param("name")String name);
    
    @Query(value = "from TblUserEntity where name like %?1%", nativeQuery = false)
    List<TblUserEntity> findUserByNameJPQL(String name);
    
    @Query(value = "select * from tb_user where name like %:name%", nativeQuery = true)
    List<TblUserEntity> findUserByNameSQL(String name);
    
}

  六、建立 junit 測試

package com.cfang.repository;

import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

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.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.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.cfang.SpringDataJpaApplication;
import com.cfang.entity.TblUserEntity;
import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {SpringDataJpaApplication.class})
@WebAppConfiguration
public class UserTest {

    @Autowired
    private UserRepository userRepository;
    
    @Test
    public void findByName() {
        TblUserEntity entity = userRepository.findByName("lisi");
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    public void findByNameAndAge() {
        TblUserEntity entity = userRepository.findByNameAndAge("lisi", 22);
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    public void findByNameLikeAndAge() {
        List<TblUserEntity> entity = userRepository.findByNameLikeAndAge("li%", 22);
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    public void readIdByJPQL() {
        TblUserEntity entity = userRepository.readIdByJPQL(27);
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    public void readIdBySQL() {
        TblUserEntity entity = userRepository.readIdBySQL(27);
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    @Transactional
    @Rollback(false)
    public void updateUserNameByIdJPQL() {
        int result = userRepository.updateUserNameByIdJPQL(27, "wangwu");
        log.info("result:{}", result);
    }
    
    @Test
    @Transactional
    @Rollback(false)
    public void updateUserNameByIdSQL() {
        int result = userRepository.updateUserNameByIdSQL(27, "wangwu2");
        log.info("result:{}", result);
    }
    
    @Test
    public void findUserByNameJPQL() {
        List<TblUserEntity> entity = userRepository.findUserByNameJPQL("li");
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    public void findUserByNameSQL() {
        List<TblUserEntity> entity = userRepository.findUserByNameSQL("li");
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    public void findUserPageByNameLike() {
        Pageable pageable = PageRequest.of(0, 2, new Sort(Sort.Direction.DESC, "id"));
        Page<TblUserEntity> entity = userRepository.findUserPageByNameLike("li%", pageable);
        log.info("result:{}", JSON.toJSONString(entity));
    }
    
    @Test
    public void testSpecification() {
        Pageable pageable = PageRequest.of(0, 2, Sort.Direction.DESC, "id");
        String name = "li";
        Specification<TblUserEntity> specification = new Specification<TblUserEntity>() {
            @Override
            public Predicate toPredicate(Root<TblUserEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> conditions = Lists.newArrayList();
                Predicate condition = null;
                condition = criteriaBuilder.like(root.get("name"), "%" + name + "%");
                conditions.add(condition);
                condition = criteriaBuilder.greaterThan(root.get("id"), 27);
                conditions.add(condition);
                
                Predicate[] arr = new Predicate[conditions.size()];
//                return criteriaBuilder.and(conditions.toArray(arr));
                query.where(conditions.toArray(arr));
                return null;
            }
        };
        Page<TblUserEntity> result = userRepository.findAll(specification, pageable);
        log.info("result:{}", JSON.toJSONString(result));
    }
    
}

  ps:在進行 repository 的CUD操做的時候,必須加事務支持 @Transactional,不然會報錯(截取部分出錯信息):

org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query

3、總結

  數據進行持久化操做的時候,都是須要一個對象來進行相關操做的,原生的Hibernate中使用Session,JPA中使用EntityManager,MyBatis中使用SqlSession。上述工程中,在主類main方法中可添加以下進行查看:

@Bean
    public Object testTransactionManager(PlatformTransactionManager manager) {
        System.out.println("TransactionManager is : " + manager.getClass().getName());
        return new Object();
    }

  SpringDataJpa提供了豐富的CRUD構造的方法,具體可根據實際狀況查詢使用。

相關文章
相關標籤/搜索