1. 程式人生 > >SpringBoot實現自定義Repository

SpringBoot實現自定義Repository

SpringBoot實現的JPA封裝了JPA的特性,只需要寫介面即可,但是有的時候約定的寫法不符合我們的開發要求,沒有很好的靈活性,這就需要我們自己去定義一下方法實現自己的封裝Repository。

借鑑網上配置:

  1. 新增所需要的依賴包
<!-- spring-data-jpa程式的啟動項依賴,底層為hibernate實現,若不使用此框架則可以依賴其他的orm框架 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId
>
spring-boot-starter-data-jpa</artifactId> </dependency> <!-- web程式的啟動項依賴,通過此依賴可引入內嵌的tomcat等web必須的jars --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mysql依賴,使用spring-data-jpa需要指定一個數據庫方言,用於連線資料庫,即mysql驅動 -->
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
  1. 定義BaseRepository介面實現JPARepository介面
package com.redsoft.spirit.dao;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

/**
 * @author
楊雷 * */
@NoRepositoryBean public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T,ID>{ public HashMap<String, Object> sqlQuery(String queryString, String countSql, Map<String, ?> values, int offset, int limit, String countName, String rowsName); public List<T> sqlQuery(String queryString, Map<String, ?> values); public List<T> sqlQuery(String queryString, Object ... values); public HashMap<String, Object> retrieve(String queryString, String countHql, Map<String, ?> values, int offset, int limit, String countName, String rowsName); }
  1. 定義BaseRepository的實現類,所有的DAO實現BaseRepository介面後,具體的實現方法由該實現類去操作。
package com.redsoft.spirit.dao;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.util.Assert;

/**
 * dao的封裝類.
 * 
 * <pre>
 *  封裝一些涉及到原生sql的增刪改查分頁等功能
 * </pre>
 * 
 * @author 楊雷
 * @since 1.0
 *
 */
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {

    //父類沒有不帶引數的構造方法,這裡手動構造父類
    public BaseRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
        System.out.println("建構函式" + domainClass);
        this.entityManager = entityManager;
        this.entityClass = domainClass;
    }

    @PersistenceContext
    private EntityManager entityManager;

    private Class<T> entityClass;

    /**
     * 查詢分頁的方法.
     * <pre>
     *  帶著條件進行動態拼接sql的查詢方法
     * </pre>
     * 
     */
    @Override
    public HashMap<String, Object> sqlQuery(String queryString, String countSql, Map<String, ?> values, int offset,
            int limit, String countName, String rowsName) {

        Assert.hasText(queryString, "queryString不能為空");

        HashMap<String, Object> map = new HashMap<String, Object>();

        Query query = entityManager.createNativeQuery(queryString);
        Query countQuery = entityManager.createNativeQuery(countSql);

        //給條件賦上值
        if (values != null && !values.isEmpty()) {
            for (Map.Entry<String, ?> entry : values.entrySet()) {  
                query.setParameter(entry.getKey(), entry.getValue());
                countQuery.setParameter(entry.getKey(), entry.getValue());    
            } 
        }

        query.setFirstResult(offset);
        query.setMaxResults(limit);
        query.unwrap(SQLQuery.class).setResultTransformer(new BeanTransformerAdapter(this.entityClass));

        Object results = query.getResultList();
        Object resultsCount = countQuery.getSingleResult();

        map.put(countName, resultsCount);
        map.put(rowsName, results);

        return map;
    }

    @Override
    public List sqlQuery(String queryString, Map<String, ?> values) {
        Session session = entityManager.unwrap(org.hibernate.Session.class);
        SQLQuery query = session.createSQLQuery(queryString);

//      //給條件賦上值
//      if (values != null && !values.isEmpty()) {
//          for (Map.Entry<String, ?> entry : values.entrySet()) {  
//              query.setParameter(entry.getKey(), entry.getValue());
//          } 
//      }

        if (values != null) {
            query.setProperties(values);
          }

        query.setResultTransformer(new BeanTransformerAdapter(this.entityClass));

        return query.list();
    }

    @Override
    public List sqlQuery(String queryString, Object... values) {
        Query query = entityManager.createNativeQuery(queryString);
//      Session session = entityManager.unwrap(org.hibernate.Session.class);
//      SQLQuery query = session.createSQLQuery(queryString);


        if (values != null) {
            for (int i = 0; i < values.length; i++) {
              query.setParameter(i + 1, values[i]);
            }
          }

        query.unwrap(SQLQuery.class).setResultTransformer(new BeanTransformerAdapter(this.entityClass));

        return query.getResultList();
    }

    @Override
    public HashMap<String, Object> retrieve(String queryString, String countHql, Map<String, ?> values, int offset,
            int limit, String countName, String rowsName) {

        HashMap<String, Object> map = new HashMap<String, Object>();

        Query query = entityManager.createQuery(queryString);
        Query countQuery = entityManager.createQuery(countHql);

        //給條件賦上值
        if (values != null && !values.isEmpty()) {
            for (Map.Entry<String, ?> entry : values.entrySet()) {  
                query.setParameter(entry.getKey(), entry.getValue());
                countQuery.setParameter(entry.getKey(), entry.getValue());    
            } 
        }

        query.setFirstResult(offset);
        query.setMaxResults(limit);

        Object results = query.getResultList();
        Object resultsCount = countQuery.getSingleResult();

        map.put(countName, resultsCount);
        map.put(rowsName, results);

        return map;
    }
}
  1. 新增將結果轉換成實體類的介面卡類
package com.redsoft.spirit.dao;

import java.beans.PropertyDescriptor;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.transform.ResultTransformer;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.StringUtils;

public class BeanTransformerAdapter<T>
  implements ResultTransformer
{
  private static final long serialVersionUID = 8149706114474043039L;
  protected final Log logger = LogFactory.getLog(getClass());
  private Class<T> mappedClass;
  private boolean checkFullyPopulated = false;

  private boolean primitivesDefaultedForNullValue = false;
  private Map<String, PropertyDescriptor> mappedFields;
  private Set<String> mappedProperties;

  public BeanTransformerAdapter()
  {
  }

  public BeanTransformerAdapter(Class<T> mappedClass)
  {
    initialize(mappedClass);
  }

  public BeanTransformerAdapter(Class<T> mappedClass, boolean checkFullyPopulated)
  {
    initialize(mappedClass);
    this.checkFullyPopulated = checkFullyPopulated;
  }

  public void setMappedClass(Class<T> mappedClass)
  {
    if (this.mappedClass == null) {
      initialize(mappedClass);
    }
    else if (!this.mappedClass.equals(mappedClass))
      throw new InvalidDataAccessApiUsageException("The mapped class can not be reassigned to map to " + 
        mappedClass + " since it is already providing mapping for " + this.mappedClass);
  }

  protected void initialize(Class<T> mappedClass)
  {
    this.mappedClass = mappedClass;
    this.mappedFields = new HashMap();
    this.mappedProperties = new HashSet();
    PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
    for (PropertyDescriptor pd : pds)
      if (pd.getWriteMethod() != null) {
        this.mappedFields.put(pd.getName().toLowerCase(), pd);
        String underscoredName = underscoreName(pd.getName());
        if (!pd.getName().toLowerCase().equals(underscoredName)) {
          this.mappedFields.put(underscoredName, pd);
        }
        this.mappedProperties.add(pd.getName());
      }
  }

  private String underscoreName(String name)
  {
    if (!StringUtils.hasLength(name)) {
      return "";
    }
    StringBuilder result = new StringBuilder();
    result.append(name.substring(0, 1).toLowerCase());
    for (int i = 1; i < name.length(); i++) {
      String s = name.substring(i, i + 1);
      String slc = s.toLowerCase();
      if (!s.equals(slc))
        result.append("_").append(slc);
      else {
        result.append(s);
      }
    }
    return result.toString();
  }

  public final Class<T> getMappedClass()
  {
    return this.mappedClass;
  }

  public void setCheckFullyPopulated(boolean checkFullyPopulated)
  {
    this.checkFullyPopulated = checkFullyPopulated;
  }

  public boolean isCheckFullyPopulated()
  {
    return this.checkFullyPopulated;
  }

  public void setPrimitivesDefaultedForNullValue(boolean primitivesDefaultedForNullValue)
  {
    this.primitivesDefaultedForNullValue = primitivesDefaultedForNullValue;
  }

  public boolean isPrimitivesDefaultedForNullValue()
  {
    return this.primitivesDefaultedForNullValue;
  }

  protected void initBeanWrapper(BeanWrapper bw)
  {
  }

  protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd)
    throws SQLException
  {
    return JdbcUtils.getResultSetValue(rs, index, pd.getPropertyType());
  }

  public static <T> BeanPropertyRowMapper<T> newInstance(Class<T> mappedClass)
  {
    BeanPropertyRowMapper newInstance = new BeanPropertyRowMapper();
    newInstance.setMappedClass(mappedClass);
    return newInstance;
  }

  public Object transformTuple(Object[] tuple, String[] aliases)
  {
    Object mappedObject = BeanUtils.instantiate(this.mappedClass);
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
    initBeanWrapper(bw);

    Set populatedProperties = isCheckFullyPopulated() ? new HashSet() : null;
    for (int i = 0; i < aliases.length; i++) {
      String column = aliases[i];
      PropertyDescriptor pd = (PropertyDescriptor)this.mappedFields.get(column.replaceAll(" ", "").toLowerCase());
      if (pd == null) continue;
      try {
        Object value = tuple[i];
        try {
          bw.setPropertyValue(pd.getName(), value);
        } catch (TypeMismatchException e) {
          if ((value == null) && (this.primitivesDefaultedForNullValue))
            this.logger.debug("Intercepted TypeMismatchException for column " + column + " and column '" + 
              column + "' with value " + value + " when setting property '" + pd.getName() + "' of type " + pd.getPropertyType() + 
              " on object: " + mappedObject);
          else {
            throw e;
          }
        }
        if (populatedProperties != null)
          populatedProperties.add(pd.getName());
      }
      catch (NotWritablePropertyException ex) {
        throw new DataRetrievalFailureException("Unable to map column " + column + 
          " to property " + pd.getName(), ex);
      }

    }

    if ((populatedProperties != null) && (!populatedProperties.equals(this.mappedProperties))) {
      throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields necessary to populate object of class [" + 
        this.mappedClass + "]: " + this.mappedProperties);
    }

    return mappedObject;
  }

  public List transformList(List list)
  {
    return list;
  }
}
  1. 擴充套件jpaRepository,讓所有的repository共享起自定義的方法。RepositoryFactoryBean負責返回一個RepositoryFactory,Spring Data Jpa將使用RepositoryFactory來建立Repository具體實現。
package com.redsoft.spirit.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;

import javax.persistence.EntityManager;
import java.io.Serializable;

/**
 * Created by konghao on 2016/12/7.
 */
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T,
        I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {

    public BaseRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
        super(repositoryInterface);
    }

    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
        return new BaseRepositoryFactory(em);
    }

    //建立一個內部類,該類不用在外部訪問
    private static class BaseRepositoryFactory<T, I extends Serializable>
            extends JpaRepositoryFactory {

        private final EntityManager em;

        public BaseRepositoryFactory(EntityManager em) {
            super(em);
            this.em = em;
        }

        //設定具體的實現類是BaseRepositoryImpl
        @Override
        protected Object getTargetRepository(RepositoryInformation information) {
            return new BaseRepositoryImpl<T, I>((Class<T>) information.getDomainType(), em);
        }

        //設定具體的實現類的class
        @Override
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
            return BaseRepositoryImpl.class;
        }
    }
}
  1. 主類上指定自己的工廠類
@SpringBootApplication
@EnableJpaRepositories(basePackages = {"com.redsoft"}, repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class)//指定自己的工廠類

public class EPIPApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(
            SpringApplicationBuilder application) {
        return application.sources(EPIPApplication.class);
    }

    public static void main(String[] args) throws InterruptedException {
        ApplicationContext ctx = SpringApplication.run(EPIPApplication.class, args);
    }
}

相關推薦

SpringBoot實現定義Repository

SpringBoot實現的JPA封裝了JPA的特性,只需要寫介面即可,但是有的時候約定的寫法不符合我們的開發要求,沒有很好的靈活性,這就需要我們自己去定義一下方法實現自己的封裝Repository。 借鑑網上配置: 新增所需要的依賴包 &l

springboot實現定義的Interceptor攔截器

構建springboot專案 這裡使用的是eclipse,簡單化 其他實體之類的程式碼省略 專案程式碼 application.properties # 資料來源配置 sp

springboot 實現定義註解

1、定義一個註解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Test { } 注意: @Target

springboot實現定義註解

1:引入jar包<!-- Spring Boot aop 代理 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactI

SpringBoot 通過定義註解實現AOP切面程式設計例項

一直心心念的想寫一篇關於AOP切面例項的博文,拖更了許久之後,今天終於著手下筆將其完成。 基礎概念 1、切面(Aspect) 首先要理解‘切’字,需要把物件想象成一個立方體,傳統的面向物件變成思維,類定義完成之後(封裝)。每次例項化一個物件,對類定義中的成員變數賦值,就相當於對這個立方體進行了一個定義,

基於springboot通過定義註解和AOP實現許可權驗證

這篇文章主要介紹自定義註解配合AOP的使用來完成一個簡單的許可權驗證的功能。 一、移入依賴 <parent> <groupId>org.springframework.boot</groupId> <artifactId>sprin

springboot自動以filter,interceptor,listener實現定義過濾、攔截、監聽

  1:自定義攔截器 (1)實現Filter介面 (2)添加註解WebFilter,指定要過濾的路徑、指定filer的名字、指定初始化引數  (3)新增@ServletComponentScan註解開啟掃描servlet元件 package top.lrshu

springboot aop 定義註解方式實現一套完善的日誌記錄(完整原始碼)

一:功能簡介 本文主要記錄如何使用aop切面的方式來實現日誌記錄功能。 主要記錄的資訊有: 操作人,方法名,引數,執行時間,操作型別(增刪改查),詳細描述,返回值。 二:專案結構圖 三:MAVEM依賴 本專案有兩個pom檔案,父類的pom檔案主要作用是對子類pom檔案依賴的版本號進行統一管理。 1.最外層

[springBoot] Springboot 整合redis並實現定義序列化遇到的問題

當我們使用@Cacheable註解的時候會將返回的物件快取起來,我們會發現預設快取的值是二進位制的,不方便檢視,為此我們自定義序列化配置,改成JSON格式的 配置如下: pom.xml <?xml version="1.0" encoding="UTF-8"?&

SpringBoot第四講擴充套件和封裝Spring Data JPA(一)_定義Repository和建立自己的BaseRepository

這一講主要介紹Spring Data JPA的封裝。和設計相關的東西都是仁者見仁,智者見智的事情,如果你有更好的封裝方案可以和我交流,互相學習。這一講會講如下一些內容 - 擴充套件Spring Data JPA實現自己的一些特殊方法 - 建立一個自己的Bas

使用SpringBoot通過定義註解+AOP+全域性異常處理實現引數統一非空校驗

一、前言         在我們寫後臺介面時,難免對引數進行非空校驗,如果一兩個還好,但如果需要寫大量的介面,及必填引數太多的時候,會給我們開發帶來大量的重複工作,及很多相似程式碼。而sping自帶的@RequestParam註解並不能完全滿足我們的需求,因為

springboot rabbitMQ 定義MessageConverter和ClassMapper實現訊息序列化

背景:公司專案使用springboot + rabbitMQ 處理訂單和推送訊息,最開始的時候,producer都是直接convertAndSend的json資料, consumer也是接收json資料,然後在轉化為Bean去處理邏輯。當然,這樣雖然沒啥大問題,但是感覺很麻煩,後來查閱文件,

SpringBoot MVC實現定義RequestBody註解

實現環境:SpringBoot 2.1.1,JDK 1.8 一、MVC實現RequestBody註解原始碼解析 1. @RequestBody 原始碼: @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.

SpringBoot使用AOP實現定義介面快取

一、引入pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data

Spring技術內幕之Spring Data JPA-定義Repository實現

1.自定義Repository方法介面,讓介面的實現類來繼承這個中間介面而不是Repository介面 package com.data.jpa.dao; import java.io.Serializable; import java.util.List; impor

SpringBoot使用定義註解實現簡單引數加密解密(註解+HandlerMethodArgumentResolver)

# 前言 > 我黃漢三又回來了,快半年沒更新部落格了,這半年來的經歷實屬不易, > 疫情當頭,本人實習的公司沒有跟員工共患難,直接辭掉了很多人。 > 作為一個實習生,本人也被無情開除了。所以本人又得重新準備找工作了。 > 算了,感慨一下,本來想昨天發的,但昨天是清明,哀悼時期,就留到了今天發。 話不多說,

如何實現定義同步組件

nds 允許 oid try unlock all 同步 while name package com.chen;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.AbstractQ

JS簡單實現定義右鍵菜單

ans idt 右鍵 動畫 忘記 span spa round 部分 RT,一個簡單的例子,僅僅講述原理 <div id="menu" style="width: 0;height: 0;background: cadetblue;position: absolu

Notification的基本用法以及使用RemoteView實現定義布局

解決 edi ngs 取消 ets lsp 過程 net tde Notification的作用 Notification是一種全局效果的通知,在系統的通知欄中顯示。既然作為通知,其基本作用有: 顯示接收到短消息、即時信息等 顯示客戶端的推送(廣告、優惠、新聞等)

Spring Boot下如何定義Repository中的DAO方法

hibernate reat 軟件測試 bst pass update pop 後綴 mark 環境配置介紹 jdk 1.8, spring Boot 1.5.3.RELEASE, MySQL, Spring Data, JPA 問題描述 Spring Data提供了一套簡