SpringBoot + Mybatis + RESTful(Jersey) 頂 原 薦

概述

Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。Spring Boot 內置了大量的常用習慣性的配置,使你無需手動配置,使用 Spring Boot 你可以不用或者只需要很少的配置就可以快速的構建你自己的項目。

MyBatis 是時下非常流行的支持定製化 SQL、存儲過程以及高級映射的優秀的持久層框架。

Jersey RESTful 框架是開源的RESTful框架, 實現了 JAX-RS 規範。它擴展了JAX-RS 參考實現, 提供了更多的特性和工具, 可以進一步地簡化 RESTful service 和 client 開發。

SpringBoot + Mybatis + RESTful(Jersey, RestEasy)可以說是時下主流的服務端WEB開發的黃金搭配,本文主要介紹如何在生產項目開發中正確的集成 Spring Boot + MyBatis + Jersey。

整合案例介紹

在一些大型的網站或者應用中,數據庫服務器單點難以支撐大的訪問壓力,升級服務器性能成本又太高,所以通用的做法是水平擴展,本案例採用多數據源的配置方式(主從配置)即主庫主要用來處理增,刪,改操作,從庫用來只讀操作,也就是常說的讀寫分離。

接下來將主要介紹多數據源的配置以及Mybatis的基本倉儲實現,由於篇幅原因這裏不一一貼出所有的代碼,具體完整案例代碼可以看這裏https://github.com/AIFEINIK/SpringBoot-Learn/tree/master/spring-boot-mybatis

案例多數據源配置詳細解釋

1、基本目錄如下,datasource包目錄下主要存放多數據源的配置類,db根目錄下的類主要是Mybatis操作的基本倉儲實現。

2、DsConfig類是主從庫的公共數據庫連接配置

public class DsConfig {

   private String name;

   // 可以在連接池中同時被分配的有效連接數的最大值,如設置爲負數,則不限制
   private int maxTotal = 8;

   // 可以在連接池中保持空閒的最大連接數,超出設置值之外的空閒連接將被回收,如設置爲負數,則不限制
   private int maxIdle = 8;

   // 可以在連接池中保持空閒的最小連接數,超出設置值之外的空閒連接將被創建,如設置爲0,則不創建
   private int minIdle = 0;

   // 當這個連接池被啓動時初始化的創建的連接個數
   private int initialSize = 0;

   // 在連接池返回連接給調用者前用來進行連接校驗的查詢sql。如果指定,則這個查詢必須是一個至少返回一行數據的SQL
   // SELECT語句。如果沒有指定,則連接將通過調用isValid() 方法進行校驗。
   private String validationQuery;

   // 指明對象是否需要通過對象驅逐者進行校驗(如果有的話),假如一個對象校驗失敗,則對象將被從連接池中釋放。
   private boolean testWhileIdle = false;

   // 指明在從連接池中租借對象時是否要進行校驗,如果對象校驗失敗,則對象將從連接池釋放,然後我們將嘗試租借另一個
   private boolean testOnBorrow = true;// false?

   // 指明在將對象歸還給連接池前是否需要校驗。
   private boolean testOnReturn = false;

   // (如果沒有可用連接)連接池在拋出異常前等待的一個連接被歸還的最大毫秒數,設置爲-1則等待時間不確定
   private long maxWaitMillis = -1;

   // 空閒對象驅逐線程運行時的休眠毫秒數,如果設置爲非正數,則不運行空閒對象驅逐線程
   private long timeBetweenEvictionRunsMillis = -1;

   // 在每個空閒對象驅逐線程運行過程中中進行檢查的對象個數。(如果有的話)
   private int numTestsPerEvictionRun = 3;

   // 符合對象驅逐對象驅逐條件的對象在池中最小空閒毫秒總數(如果有的話)
   private long minEvictableIdleTimeMillis = 1000 * 60 * 30;

   set{...}
   get{...}

}

3、MasterDsConfig類是主庫的連接配置

@Configuration
@ConfigurationProperties("datasource.master")
public class MasterDsConfig extends DsConfig {
    //這裏可以寫master庫自己的屬性
}

注:@ConfigurationProperties 註解可以用來讀取配置文件中的值然後注入到類屬性中

4、MasterDataSourceConfig類爲主庫數據源配置

@Configuration
public class MasterDataSourceConfig {

    @Value("${mybatis.config-location}")
    private String mybatisConfigLocation;

    @Autowired
    private MasterDsConfig config;

    @Primary
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "datasource.master")
    public DataSource masterDataSource() {
        BasicDataSource ds = (BasicDataSource) DataSourceBuilder.create().type(BasicDataSource.class).build();
        ds.setMaxTotal(config.getMaxTotal());
        ds.setMaxIdle(config.getMaxIdle());
        ds.setMinIdle(config.getMinIdle());
        ds.setInitialSize(config.getInitialSize());
        ds.setValidationQuery(config.getValidationQuery());
        ds.setTestWhileIdle(config.isTestWhileIdle());
        ds.setTestOnBorrow(config.isTestOnBorrow());
        ds.setTestOnReturn(config.isTestOnReturn());
        ds.setMaxWaitMillis(config.getMaxWaitMillis());
        // ds.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis());
        // ds.setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun());
        // ds.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis());
        ds.setDefaultAutoCommit(false);
        return ds;
    }

    @Bean(name = "masterTransactionManager")
    public DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "masterSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource)
            throws Exception {
        String path = mybatisConfigLocation.replace("classpath:", "/");
        ClassPathResource resource = new ClassPathResource(path);
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setConfigLocation(resource);
        return factory.getObject();
    }

    @Bean(name = "masterSqlSessionTemplate")
    public SqlSessionTemplate masterSqlSessionTemplate(
            @Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

 

注:1、@Primary 註解表示當有多個相同的Bean時,優先使用該Bean。

       2、@ConfigurationProperties 註解到@Bean註解的方法上表示將配置文件中前綴爲datasource.master的值注入到該Bean對應的屬性字段上。如這裏將application.yml配置文件中前綴爲datasource.master的值注入到了BasicDataSource實例的對應屬性字段中。

從庫數據源配置與主庫數據源配置基本相同,這裏不再介紹。

Mybatis數據庫操作的基本倉儲實現

1、EntityRepository 倉儲訪問接口, 提供通用倉儲方法

public interface EntityRepository<T extends Entity> {

   // 添加一個實體
   public T insert(T t) throws RepositoryException;

   // gen一個實體
   public int update(T t) throws RepositoryException;

   // 添加一個實體(update and insert)
   public T save(T t) throws RepositoryException;

   // 更新一個實體
   public int updateSpecify(T t) throws RepositoryException;

   // 移除一個實體
   public int delete(T t) throws RepositoryException;

   // 根據實體ID,刪除實體
   public int deleteById(Integer id) throws RepositoryException;

   // 根據實體ID,查找實體
   public T getById(Integer id);

   // 查詢符合查詢參數的實體結果集數量
   public int findResultCount(QueryParameters param);

   // 查詢符合查詢參數的實體結果集
   public List<T> findResults(QueryParameters param);

}

 2、MybatisEntityRepository基於MyBatis的基本倉儲實現

public abstract class MybatisEntityRepository<T extends Entity> implements EntityRepository<T> {

   protected final static Logger logger = LoggerFactory.getLogger(MybatisEntityRepository.class);

   protected SqlSessionFactory sqlSessionFactory;

   protected abstract void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory);

   //自定義Mybatis的sql配置文件中的訪問命名空間
   protected abstract String nameSpaceForSqlId();

   //自定義Mybatis的sql配置文件中的SqlId
   protected String fullSqlId(String sqlId) {
      return nameSpaceForSqlId() + "." + sqlId;
   }

   @Override
   public T insert(T t) throws RepositoryException {
      try (SqlSession session = sqlSessionFactory.openSession()) {
         session.insert(fullSqlId("insert"), t);
         session.commit(true);
      } catch (Exception e) {
         logger.error("Insert Entity failed: ", e);
         throw new RepositoryException(e);
      }
      return t;
   }

   @Override
   public int update(T t) throws RepositoryException {
      int ret = 0;
      try (SqlSession session = sqlSessionFactory.openSession()) {
         ret = session.update(fullSqlId("update"), t);
         session.commit(true);
      } catch (Exception e) {
         logger.error("Update Entity failed: ", e);
         throw new RepositoryException(e);
      }
      return ret;
   }

   @Override
   public T save(T t) throws RepositoryException {
      try (SqlSession session = sqlSessionFactory.openSession()) {
         int ret = 0;
         if (t.getId() != null) {
            ret = session.update(fullSqlId("update"), t);
         }
         if (ret == 0) {
            ret = session.insert(fullSqlId("insert"), t);
         }
         session.commit(true);
      } catch (Exception e) {
         logger.error("Save/Update Entity failed: ", e);
         throw new RepositoryException(e);
      }
      return t;
   }

   @Override
   public int updateSpecify(T t) throws RepositoryException {
      int count = 0;
      try (SqlSession session = sqlSessionFactory.openSession()) {
         count = session.update(fullSqlId("updateSpecify"), t);
         session.commit(true);
      } catch (Exception e) {
         logger.error("UpdateSpecify Entity failed: ", e);
         throw new RepositoryException(e);
      }
      return count;
   }

   @Override
   public int deleteById(Integer id) throws RepositoryException {
      int count = 0;
      try (SqlSession session = sqlSessionFactory.openSession()) {
         count = session.delete(fullSqlId("deleteById"), id);
         session.commit(true);
      } catch (Exception e) {
         logger.error("Remove Entity failed: ", e);
         throw new RepositoryException(e);
      }
      return count;
   }

   @Override
   public int delete(T t) throws RepositoryException {
      int count = 0;
      try (SqlSession session = sqlSessionFactory.openSession()) {
         count = session.delete(fullSqlId("delete"), t);
         session.commit(true);
      } catch (Exception e) {
         logger.error("Remove Entity failed: ", e);
         throw new RepositoryException(e);
      }
      return count;
   }

   @Override
   public T getById(Integer id) {
      try (SqlSession session = sqlSessionFactory.openSession()) {
         return session.selectOne(fullSqlId("getById"), id);
      }
   }

   @Override
   public int findResultCount(QueryParameters params) {
      if (params == null) { // 糾錯
         params = new QueryParameters();
      }
      try (SqlSession session = sqlSessionFactory.openSession()) {
         return session.selectOne(fullSqlId("findResultCount"), params);
      }
   }

   @Override
   public List<T> findResults(QueryParameters params) {
      if (params == null) { // 糾錯
         params = new QueryParameters();
      }
      try (SqlSession session = sqlSessionFactory.openSession()) {
         return session.selectList(fullSqlId("findResults"), params);
      }
   }

}

3、MasterMybatisEntityRepository 主庫的Mybatis倉儲

public abstract class MasterMybatisEntityRepository<T extends Entity> extends MybatisEntityRepository<T> {

    @Autowired
    @Qualifier("masterSqlSessionFactory")
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }
}

4、SlaveMybatisEntityRepository 從庫的Mybatis倉儲

public abstract class SlaveMybatisEntityRepository<T extends Entity> extends MybatisEntityRepository<T> {

    @Autowired
    @Qualifier("slaveSqlSessionFactory")
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }
}

5、最後就可以編寫repo類來通過Myabtis基礎倉儲訪問數據庫了

@Repository
public class PersonRepo extends MasterMybatisEntityRepository<Person> {

    @Override
    protected String nameSpaceForSqlId() {
        //這裏配置的namespace與PersonMapper.xml中的namespace要保持一致
        return "com.os.china.mapper.PersonMapper";
    }

    /**
     * Mybatis基本倉儲中沒有的可以自己寫
     * @param userName
     * @return
     */
    public Person getPersonByName(String userName) {
        try (SqlSession session = sqlSessionFactory.openSession()) {
            return session.selectOne(fullSqlId("getPersonByName"), userName);
        }
    }
}

集成 RESTful 框架 Jersey

1、Maven導入Jersey包

<!-- 使用jersey RESTful 框架 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jersey</artifactId>
    <version>${boot.version}</version>
</dependency>

2、編寫Jersey配置類

@Configuration
public class JerseyConfig extends ResourceConfig {

   /**
    * 掃描com.os.china包,使其識別JAX-RS註解
    */
   public JerseyConfig() {
      packages("com.os.china");
   }

}

RESTful風格的資源請求處理類編寫

@Component
@Path("personMgr")
public class PersonMgrResource {

    @Autowired
    private PersonService personService;

    @GET
    @Path("getPersonById")
    @Produces(MediaType.APPLICATION_JSON)
    public JsonResp getPersonById(@QueryParam("id") Integer id) {
        Person person = personService.getPersonById(id);
        return JsonResp.success(person);
    }

    @GET
    @Path("getPersonByName")
    @Produces(MediaType.APPLICATION_JSON)
    public JsonResp getPersonByName(@QueryParam("userName") String userName) {
        Person person = personService.getPersonByName(userName);
        return JsonResp.success(person);
    }

    /**
     * 分頁查詢用戶信息
     * @param req 封裝分頁參數
     * @return
     */
    @POST
    @Path("getPersons")
    @Produces(MediaType.APPLICATION_JSON)
    public PageResp getAllPersons(@Valid PageReq req) {
        QueryParameters params = new QueryParameters(req);
        int total = personService.getPersonCount(params);
        if (total == 0) {
            return PageResp.emptyResult();
        }

        PageInfo page = new PageInfo(req.getPage(), req.getCount(), total);
        params.setPage(page);
        List<Person> persons = personService.getPersons(params);
        return PageResp.success(total, persons);
    }

}

測試

在瀏覽器中使用postman插件來測試

總結

本文主要介紹了 SpringBoot 集成 Mybatis 多數據源的配置,以及如何集成 RESTful 框架 Jersey,最後通過簡單測試來驗證整個流程是否流轉成功,完整代碼可在GitHub中查看,如有任何疑問,請賜教!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章