(六)SpringBoot+SpringCloud —— 集成MyBatis

主要是MyBatis與SpringBoot的繼承,此處與SpringCloud無關,做爲記錄。前端

依賴與配置

數據庫使用的是MySql,因此須要加入如下的依賴:java

<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

配置文件application.yml以下:mysql

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 7001
spring:
  application:
    name: SERVICE-USER
  datasource: #數據源
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/owl-bookstore?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
    username: root
    password: root
    dbcp2: #dbcp2鏈接池
      max-idle: 10
mybatis:
  #實體類的包
  type-aliases-package: cn.net.bysoft.owl.bookstore.facade.user.entity
  #mapper.xml存放的路徑
  mapperLocations: classpath:mapper/*.xml
  #mybatis配置文件存放的路徑
  configLocation: classpath:mybatis-config.xml

實現代碼

經過上面的依賴和配置,基本能夠用本身熟悉的方式使用Mybatis了。git

下面來記錄一下我本身的實現,實現很簡單,沒有考慮過多的東西。github

能夠採用註解的方式使用mybatis,可是此處我使用的是比較原始的方法,配置mapper來使用mybatis。spring

  1. 定義一個BaseDao接口和BaseDaoImpl實現類,編寫一些通用的Dao方法。
  2. 定義一個BaseEntity實體類,一樣定義一些經常使用的方法。
  3. 分頁採用了Mybatis的攔截器實現。

直接貼出代碼,BaseEntity類:sql

package cn.net.bysoft.owl.bookstore.common.entity;

import java.io.Serializable;

public abstract class BaseEntity implements Serializable {

	private static final long serialVersionUID = -1105239108084459358L;
	private Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

}

分頁的實體類:數據庫

package cn.net.bysoft.owl.bookstore.common.entity;

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

/**
 * 分頁組件。<br>
 * 用於前端DataGrid控件初始化。<br>
 * 組件內包含當前頁、每頁顯示條數、總記錄數、數據、總頁數、頁碼索引等信息。
 * 
 * @author XuePeng
 */
public class PageBean<T> implements Serializable {

    private static final long serialVersionUID = 8470697978259453214L;

    private int currentPage;
    private int numPerPage;
    private int totalCount;
    private List<T> recordList;

    private int pageCount;
    private int beginPageIndex;
    private int endPageIndex;

    public PageBean(int currentPage, int numPerPage, int totalCount, List<T> recordList) {
        this.currentPage = currentPage;
        this.numPerPage = numPerPage;
        this.totalCount = totalCount;
        this.recordList = recordList;
        // 計算總頁碼
        pageCount = (totalCount + numPerPage - 1) / numPerPage;
        // 計算 beginPageIndex 和 endPageIndex
        // 總頁數很少於10頁,則所有顯示
        if (pageCount <= 10) {
            beginPageIndex = 1;
            endPageIndex = pageCount;
        }
        // 總頁數多於10頁,則顯示當前頁附近的共10個頁碼
        else {
            // 當前頁附近的共10個頁碼(前4個 + 當前頁 + 後5個)
            beginPageIndex = currentPage - 4;
            endPageIndex = currentPage + 5;
            // 當前面的頁碼不足4個時,則顯示前10個頁碼
            if (beginPageIndex < 1) {
                beginPageIndex = 1;
                endPageIndex = 10;
            }
            // 當後面的頁碼不足5個時,則顯示後10個頁碼
            if (endPageIndex > pageCount) {
                endPageIndex = pageCount;
                beginPageIndex = pageCount - 10 + 1;
            }
        }
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getNumPerPage() {
        return numPerPage;
    }

    public void setNumPerPage(int numPerPage) {
        this.numPerPage = numPerPage;
    }

    /**
     * @return 得到總記錄數。
     */
    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public List<T> getRecordList() {
        return recordList;
    }

    public void setRecordList(List<T> recordList) {
        this.recordList = recordList;
    }

    public int getPageCount() {
        return pageCount;
    }

    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }

    public int getBeginPageIndex() {
        return beginPageIndex;
    }

    public void setBeginPageIndex(int beginPageIndex) {
        this.beginPageIndex = beginPageIndex;
    }

    public int getEndPageIndex() {
        return endPageIndex;
    }

    public void setEndPageIndex(int endPageIndex) {
        this.endPageIndex = endPageIndex;
    }

}
package cn.net.bysoft.owl.bookstore.common.entity;

import java.io.Serializable;

/**
 * 分頁參數的實體類。<br>
 * 設置對象的當前頁數和每頁記錄數,進行數據庫分頁查詢。
 * 
 * @author XuePeng
 */
public class PageParam implements Serializable {

    private static final long serialVersionUID = 6297178964005032338L;
    private int pageNum;
    private int numPerPage;

    public PageParam(int pageNum, int numPerPage) {
        this.pageNum = pageNum;
        this.numPerPage = numPerPage;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getNumPerPage() {
        return numPerPage;
    }

    public void setNumPerPage(int numPerPage) {
        this.numPerPage = numPerPage;
    }

}

接下來,BaseDao接口,定義了CURD的基本方法,和經過分頁查詢的方法,具體以下:apache

package cn.net.bysoft.owl.bookstore.common.core.dao;

import java.util.List;
import java.util.Map;

import cn.net.bysoft.owl.bookstore.common.entity.BaseEntity;
import cn.net.bysoft.owl.bookstore.common.entity.PageBean;
import cn.net.bysoft.owl.bookstore.common.entity.PageParam;

public interface BaseDao<T extends BaseEntity> {

    long insert(T entity);

    int update(T entity);

    int deleteById(long id);

    int deleteByIds(long... id);

    T findById(long id);

    T findByParam(Map<String, Object> paramMap);

    List<T> listByParam(Map<String, Object> paramMap);

    PageBean<T> listByPageAndParam(PageParam pageParam, Map<String, Object> paramMap);

    PageBean<T> listByPageAndParam(PageParam pageParam, Map<String, Object> paramMap, String sqlId);

    long countByParam(Map<String, Object> paramMap);

}

BaseDaoImpl類,實現了BaseDao接口:緩存

package cn.net.bysoft.owl.bookstore.common.core.dao;

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

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;

import cn.net.bysoft.owl.bookstore.common.entity.BaseEntity;
import cn.net.bysoft.owl.bookstore.common.entity.PageBean;
import cn.net.bysoft.owl.bookstore.common.entity.PageParam;

public abstract class BaseDaoImpl<T extends BaseEntity> extends SqlSessionDaoSupport implements BaseDao<T> {

    private static final String SQL_INSERT = "insert";
    private static final String SQL_UPDATE = "update";
    private static final String SQL_DELETE_BY_ID = "deleteById";
    private static final String SQL_DELETE_BY_IDS = "deleteByIds";
    private static final String SQL_FIND_BY_ID = "findById";
    private static final String SQL_FIND_BY_PARAM = "findByParam";
    private static final String SQL_LIST_BY_PARAM = "listByParam";
    private static final String SQL_LIST_BY_PAGE = "listByPageAndParam";
    private static final String SQL_COUNT_BY_PARAM = "countByParam";

    // SqlSessionTemplate實例(要求Spring中進行SqlSessionTemplate的配置)
    // 能夠調用sessionTemplate完成數據庫操做
    private SqlSessionTemplate sessionTemplate;

    // SqlSessionFactory實例(要求Spring中進行SqlSessionFactory的配置)
    // 能夠調用sessionFactory打開一個SqlSession
    private SqlSessionFactory sessionFactory;

    public SqlSessionTemplate getSessionTemplate() {
        return this.sessionTemplate;
    }

    @Autowired
    public void setSessionTemplate(SqlSessionTemplate sessionTemplate) {
        super.setSqlSessionTemplate(sessionTemplate);
        this.sessionTemplate = sessionTemplate;
    }

    public SqlSessionFactory getSessionFactory() {
        return this.sessionFactory;
    }

    @Override
    @Autowired
    public void setSqlSessionFactory(SqlSessionFactory sessionFactory) {
        super.setSqlSessionFactory(sessionFactory);
        this.sessionFactory = sessionFactory;
    }

    @Override
    public long insert(T entity) {
        // 對數據庫進行insert操做。
        // 執行Mapper配置文件中的insert方法。
        int result = sessionTemplate.insert(getStatement(SQL_INSERT), entity);
        // 判斷若是要新建的實體對象不爲null,而且成功保存到數據庫了,則返回其主鍵。
        if (entity != null && entity.getId() != null && result > 0) {
            return entity.getId();
        }
        return result;
    }

    @Override
    public int update(T entity) {
        // 對數據庫進行update操做,並返回影響行數。
        // 執行Mapper配置文件中的update方法。
        return sessionTemplate.update(getStatement(SQL_UPDATE), entity);
    }

    @Override
    public int deleteById(long id) {
        // 對數據庫進行刪除操做。
        // 執行Mapper配置文件中的deleteById方法。
        return sessionTemplate.delete(getStatement(SQL_DELETE_BY_ID), id);
    }

    @Override
    public int deleteByIds(long... ids) {
        return sessionTemplate.delete(getStatement(SQL_DELETE_BY_IDS), ids);
    }

    @Override
    public T findById(long id) {
        // 對數據庫進行查詢操做。
        // 執行Mapper配置文件中的findById方法。
        return sessionTemplate.selectOne(getStatement(SQL_FIND_BY_ID), id);
    }

    @Override
    public T findByParam(Map<String, Object> param) {
        // 對數據庫進行查詢操做。
        // 執行Mapper配置文件中的findByParam方法。
        if (param == null || param.isEmpty()) {
            return null;
        }
        return sessionTemplate.selectOne(getStatement(SQL_FIND_BY_PARAM), param);
    }

    @Override
    public List<T> listByParam(Map<String, Object> param) {
        // 對數據庫進行查詢操做。
        // 執行Mapper配置文件中的findListByParam方法。
        return sessionTemplate.selectList(getStatement(SQL_LIST_BY_PARAM), param);
    }

    @Override
    public PageBean<T> listByPageAndParam(PageParam pageParam, Map<String, Object> param) {
        // 對數據庫進行查詢操做。
        // 執行Mapper配置文件中的findListByPageAndParam方法。
        HashMap<String, Object> params;
        if (param == null) {
            params = new HashMap<>();
        } else {
            params = (HashMap<String, Object>) param;
        }

        // 獲取分頁數據集 , 注切勿換成 sessionTemplate 對象。
        // 使用RowBounds進行分頁。
        List<T> list = getSqlSession().selectList(getStatement(SQL_LIST_BY_PAGE), params,
                new RowBounds((pageParam.getPageNum() - 1) * pageParam.getNumPerPage(), pageParam.getNumPerPage()));

        // 統計總記錄數
        Object countObject = (Object) getSqlSession().selectOne(getStatement(SQL_LIST_BY_PAGE),
                new ExecutorInterceptor.CountParameter(params));
        Long count = Long.valueOf(countObject.toString());

        return new PageBean<>(pageParam.getPageNum(), pageParam.getNumPerPage(), count.intValue(), list);
    }

    @Override
    public PageBean<T> listByPageAndParam(PageParam pageParam, Map<String, Object> param, String sqlId) {
        // 對數據庫進行查詢操做。
        // 執行Mapper配置文件中的自定義的方法。
        HashMap<String, Object> params;
        if (param == null) {
            params = new HashMap<>();
        } else {
            params = (HashMap<String, Object>) param;
        }

        // 獲取分頁數據集 , 注切勿換成 sessionTemplate 對象
        List<T> list = getSqlSession().selectList(getStatement(sqlId), params,
                new RowBounds((pageParam.getPageNum() - 1) * pageParam.getNumPerPage(), pageParam.getNumPerPage()));

        // 統計總記錄數
        Object countObject = (Object) getSqlSession().selectOne(getStatement(sqlId),
                new ExecutorInterceptor.CountParameter(params));
        Long count = Long.valueOf(countObject.toString());

        return new PageBean<>(pageParam.getPageNum(), pageParam.getNumPerPage(), count.intValue(), list);
    }

    @Override
    public long countByParam(Map<String, Object> param) {
        // 對數據庫進行查詢操做。
        // 執行Mapper配置文件中的findCountByParam方法。
        return sessionTemplate.selectOne(getStatement(SQL_COUNT_BY_PARAM), param);
    }

    protected String getStatement(String sqlId) {
        String name = this.getClass().getName();
        StringBuilder sb = new StringBuilder();
        sb.append(name).append(".").append(sqlId);
        return sb.toString();
    }

}

用於分頁的攔截器類:

package cn.net.bysoft.owl.bookstore.common.core.dao;

import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;

public abstract class AbstractInterceptor implements Interceptor {

    protected MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource, boolean isCount) {
        Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource,
                ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        String[] s = ms.getKeyProperties();
        if (s == null) {
            builder.keyProperty(null);
        } else {
            builder.keyProperty(s[0]);
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        if (isCount) {
            List<ResultMap> resultMaps = new ArrayList<>();
            resultMaps.add(new ResultMap.Builder(ms.getConfiguration(), ms.getId(), Integer.class,
                    new ArrayList<ResultMapping>()).build());
            builder.resultMaps(resultMaps);
        } else {
            builder.resultMaps(ms.getResultMaps());
        }
        builder.cache(ms.getCache());
        return builder.build();
    }

    public static class BoundSqlSqlSource implements SqlSource {
        BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }

}
package cn.net.bysoft.owl.bookstore.common.core.dao;

public interface Dialect {

    default boolean supportsLimit() {
        return false;
    }

    default boolean supportsLimitOffset() {
        return supportsLimit();
    }

    default String getLimitString(String sql, int offset, int limit) {
        return getLimitString(sql, offset, Integer.toString(offset), limit, Integer.toString(limit));
    }

    String getLimitString(String sql, int offset, String offsetPlaceholder, int limit, String limitPlaceholder);
    
}
package cn.net.bysoft.owl.bookstore.common.core.dao;

import java.util.Properties;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.log4j.Logger;

@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
        RowBounds.class, ResultHandler.class }) })
public class ExecutorInterceptor extends AbstractInterceptor {

    private static final Logger LOGGER = Logger.getLogger(ExecutorInterceptor.class);
    private static final int MAPPED_STATEMENT_INDEX = 0;
    private static final int PARAMETER_INDEX = 1;
    private static final int ROWBOUNDS_INDEX = 2;

    private static final String ORDER_BY = "order by";

    private Dialect dialect;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        processIntercept(invocation.getArgs());
        return invocation.proceed();
    }

    private void processIntercept(final Object[] queryArgs) {
        MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
        Object parameter = queryArgs[PARAMETER_INDEX];
        final RowBounds rowBounds = (RowBounds) queryArgs[ROWBOUNDS_INDEX];
        int offset = rowBounds.getOffset();
        int limit = rowBounds.getLimit();
        // 分頁
        if (dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {
            BoundSql boundSql = ms.getBoundSql(parameter);

            String sql = boundSql.getSql().replaceAll("\\s{2,}", " ").trim();
            if (dialect.supportsLimitOffset()) {
                sql = dialect.getLimitString(sql, offset, limit);
                offset = RowBounds.NO_ROW_OFFSET;
            } else {
                sql = dialect.getLimitString(sql, 0, limit);
            }

            limit = RowBounds.NO_ROW_LIMIT;

            queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset, limit);
            BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(),
                    boundSql.getParameterObject());
            MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql), false);
            queryArgs[MAPPED_STATEMENT_INDEX] = newMs;

            LOGGER.debug("==>" + sql);

        } else if (parameter instanceof CountParameter) {
            // 獲取總數
            parameter = ((CountParameter) parameter).getParameter();
            BoundSql boundSql = ms.getBoundSql(parameter);

            String sql = boundSql.getSql().replaceAll("\\s{2,}", " ").replace(" FROM", " from")
                    .replace("ORDER BY", ORDER_BY).replace("GROUP BY", ORDER_BY).trim();

            if (sql.split("from").length > 2 || sql.split(ORDER_BY).length > 2 || sql.indexOf(ORDER_BY) > -1) {
                sql = "select count(1) from (" + sql + ") tmp";
            } else {
                int fromIndex = sql.indexOf(" from");
                sql = "select count(1)" + sql.substring(fromIndex);

                int orderByIndex = sql.indexOf(ORDER_BY);
                if (orderByIndex > 0) {
                    sql = sql.substring(0, orderByIndex);
                }
            }

            BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(),
                    boundSql.getParameterObject());
            MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql), true);
            queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
            queryArgs[PARAMETER_INDEX] = parameter;

            LOGGER.debug("==>" + sql);
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        String dialectClass = properties.getProperty("dialectClass");
        try {
            dialect = (Dialect) Class.forName(dialectClass).newInstance();
        } catch (Exception e) {
            throw new RuntimeException("方言加載屬性異常");
        }

    }

    public static class CountParameter {
        private Object parameter;

        public CountParameter(Object parameter) {
            this.parameter = parameter;
        }

        public Object getParameter() {
            return parameter;
        }
    }

}
package cn.net.bysoft.owl.bookstore.common.core.dao;

public class MySqlDialect implements Dialect {

    @Override
    public String getLimitString(String sql, int offset, String offsetPlaceholder, int limit, String limitPlaceholder) {
        StringBuilder stringBuilder = new StringBuilder(sql);

        stringBuilder.append(" limit ");
        if (offset > 0) {
            stringBuilder.append(offsetPlaceholder);
            stringBuilder.append(",");
        }
        stringBuilder.append(limitPlaceholder);
        return stringBuilder.toString();
    }
}

最後是Mapper和Mybatis.Config配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.net.bysoft.owl.bookstore.service.user.dao.impl.UserDaoImpl">

	<sql id="condition_sql">
		<if test="email != null and email != ''">
			email = #{email}
		</if>
	</sql>

	<resultMap id="userMap" type="User">
		<id column="id" property="id" />
		<result property="email" column="email" />
		<result property="mobile" column="mobile" />
		<result property="password" column="pwd" />
	</resultMap>

	<insert id="insert" parameterType="User" keyProperty="id"
		useGeneratedKeys="true">
		INSERT INTO obs_user (email, mobile, pwd) VALUES
		(#{email}, #{mobile}, #{password})
	</insert>

	<select id="findById" parameterType="Long" resultMap="userMap">
		SELECT id, email, mobile FROM obs_user
		<where>
		  id = #{id}
		</where>
	</select>

	<select id="countByParam" parameterType="java.util.Map"
		resultType="Long">
		SELECT COUNT(0) FROM obs_user
		<where>
			<include refid="condition_sql" />
		</where>
	</select>

</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 引用系統全局配置文件 -->
	<settings>
		<!-- 這個配置使全局的映射器啓用或禁用 緩存 -->
		<setting name="cacheEnabled" value="true" />
		<!-- 全局啓用或禁用延遲加載。當禁用時, 全部關聯對象都會即時加載 -->
		<setting name="lazyLoadingEnabled" value="false" />
		<!-- 容許或不容許多種結果集從一個單獨 的語句中返回(須要適合的驅動) -->
		<setting name="multipleResultSetsEnabled" value="true" />
		<!-- 使用列標籤代替列名。 不一樣的驅動在這 方便表現不一樣。 參考驅動文檔或充分測 試兩種方法來決定所使用的驅動 -->
		<setting name="useColumnLabel" value="true" />
		<!-- 容許 JDBC 支持生成的鍵。 須要適合的 驅動。 若是設置爲 true 則這個設置強制 生成的鍵被使用, 儘管一些驅動拒絕兼 容但仍然有效(好比 
			Derby) -->
		<setting name="useGeneratedKeys" value="false" />
		<!-- 配置默認的執行器。SIMPLE 執行器沒 有什麼特別之處。REUSE 執行器重用 預處理語句。BATCH 執行器重用語句 和批量更新 -->
		<setting name="defaultExecutorType" value="SIMPLE" />
		<!-- 設置超時時間, 它決定驅動等待一個數 據庫響應的時間 -->
		<setting name="defaultStatementTimeout" value="100" />
		<setting name="safeRowBoundsEnabled" value="false" />
		<setting name="mapUnderscoreToCamelCase" value="false" />
		<setting name="localCacheScope" value="SESSION" />
		<setting name="jdbcTypeForNull" value="OTHER" />
		<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
		<!-- 打印查詢語句 -->  
        <setting name="logImpl" value="STDOUT_LOGGING" />
	</settings>
</configuration>

Github

https://github.com/XuePeng87/owl-bookstore

相關文章
相關標籤/搜索