1. 程式人生 > >Java基於jdbctemplate資料持久層操作封裝

Java基於jdbctemplate資料持久層操作封裝

相關資源分享-下載可直接使用:

https://download.csdn.net/download/csdn_heliu/10736181

JdbcTemplate簡介:

  1. spring對資料庫的操作在jdbc上面做了深層次的封裝,使用spring的注入功能,可以把DataSource註冊到JdbcTemplate之中。
  2. JdbcTemplate主要提供以下五類方法:
  • execute方法:可以用於執行任何SQL語句,一般用於執行DDL語句;
  • update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語句;
  • query方法及queryForXXX方法:用於執行查詢相關語句;
  • call方法:用於執行儲存過程、函式相關語句。

為啥要使用JdbcTemplate進行開發呢?

  1. Spring提供的JdbcTemplate對jdbc做了封裝,大大簡化了資料庫的操作。
  2. 如果直接使用JDBC的話,需要我們載入資料庫驅動、建立連線、釋放連線、異常處理等一系列的動作,繁瑣且程式碼看起來不直觀。
  3. Spring提供的JdbcTempate能直接資料物件對映成實體類,不再需要獲取ResultSet去獲取值/賦值等操作,提高開發效率。

資料庫相關配置:

# 資料庫配置
jdbc.url=jdbc\:mysql\://localhost\:3306/test?useUnicode\=true&characterEncoding\=utf-8
jdbc.username=root
jdbc.password=root
jdbc.driver=com.mysql.jdbc.Driver

資料來源相關配置application-datasource.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
   http://www.springframework.org/schema/aop
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/mvc  
     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"
       default-lazy-init="true">

    <description>資料來源相關配置</description>

    <!-- 與上面的配置等價,下面的更容易理解 -->
    <!--   <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations"> &lt;!&ndash; PropertyPlaceholderConfigurer類中有個locations屬性,接收的是一個數組,即我們可以在下面配好多個properties檔案 &ndash;&gt;
                <array>
                    <value>classpath:application.properties</value>
                </array>
            </property>
        </bean>-->

    <!-- 這些配置Spring在啟動時會初始化-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="namedJdbcTemplate"
          class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionTemplate"
          class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
    </bean>

    <bean id="daoClient" class="com.heliu.base.DaoClient">
        <property name="jdbcTemplate" ref="namedJdbcTemplate"/>
        <property name="xmlParse" ref="xmlParse"/>
    </bean>

    <bean id="xmlParse" class="com.heliu.base.XmlParse">
        <property name="resources" value="classpath:sqlmaps/sqlMap_*.xml"/>
    </bean>

</beans>

編寫封裝類DaoClient.java:

package com.heliu.base;

import com.alibaba.fastjson.JSONObject;
import com.heliu.util.FreeMakerParser;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

import java.util.*;

/**
 * @author HeLiu
 * @Description 基於jdbctemplate資料持久層操作封裝
 * @date 2018/7/30 10:00
 */
public class DaoClient {

    private static final Logger logger = LoggerFactory.getLogger(DaoClient.class);

    private static final String INTEGER = "java.lang.Integer";
    private static final String LONG = "java.lang.Long";
    private static final String BIGDECIMAL = "java.math.BigDecimal";
    private static List<String> TYPE_LIST = new LinkedList<>();

    static {
        TYPE_LIST.add("java.lang.Integer");
        TYPE_LIST.add("java.lang.String");
        TYPE_LIST.add("java.lang.Long");
        TYPE_LIST.add("java.math.BigDecimal");
    }

    private FreeMakerParser freeMakerParser;

    private NamedParameterJdbcTemplate jdbcTemplate;

    private XmlParse xmlParse;

    public NamedParameterJdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(NamedParameterJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public XmlParse getXmlParse() {
        return xmlParse;
    }

    public void setXmlParse(XmlParse xmlParse) {
        this.xmlParse = xmlParse;
    }

    /**
     * @Description 查詢單個物件
     * @author HeLiu
     * @date 2018/7/30 10:22
     */
    public <T> T queryForObject(String sqlId, Object param, Class<T> clazz) {
        String clazzStr = clazz.getName();
        Map<String, Object> paramMap = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        String sql = xmlParse.getSql(sqlId);
        sql = freeMakerParser.process(sql, paramMap);

        try {
            //返回一些特殊型別(Integer,String,... ...)
            if (TYPE_LIST.contains(clazzStr)) {
                return jdbcTemplate.queryForObject(sql, paramMap, clazz);
            }

            //返回指定物件,將返回引數對映到物件裡面
            RowMapper<T> rm = BeanPropertyRowMapper.newInstance(clazz);
            return jdbcTemplate.queryForObject(sql, paramMap, rm);

        } catch (EmptyResultDataAccessException e) {
            logger.info(".......databse no record .........");
            return null;
        }
    }

    /**
     * @Description 查詢單個物件(不要條件查詢,比如查詢數量... ...)
     * @author HeLiu
     * @date 2018/7/30 11:54
     */
    public <T> T queryForObject(String sqlId, Class<T> clazz) {
        return queryForObject(sqlId, new HashMap<>(), clazz);
    }

    /**
     * @Description 查詢Map結果
     * @author HeLiu
     * @date 2018/7/30 14:29
     */
    public Map<String, Object> queryForMap(String sqlId, Object param) {
        Map paramMap = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        String sql = xmlParse.getSql(sqlId);
        sql = freeMakerParser.process(sql, paramMap);

        try {
            return jdbcTemplate.queryForMap(sql, paramMap);
        } catch (EmptyResultDataAccessException e) {
            logger.info(".......databse no record .........");
            return null;
        }
    }

    /**
     * @Description 查詢多個物件
     * @author HeLiu
     * @date 2018/7/30 17:14
     */
    public <T> List<T> queryForList(String sqlId, Object param, Class<T> clazz) {
        String clazzStr = clazz.getName();
        Map paramMap = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        String sql = xmlParse.getSql(sqlId);
        sql = freeMakerParser.process(sql, paramMap);

        try {
            //jdbcTemplate查詢String、Integer... ...型別query方法會報錯
            //所以通過呼叫其他方法解決
            //所以在此區分一下
            if (TYPE_LIST.contains(clazzStr)) {
                return jdbcTemplate.queryForList(sql, paramMap, clazz);
            }

            RowMapper<T> rm = BeanPropertyRowMapper.newInstance(clazz);
            return jdbcTemplate.query(sql, paramMap, rm);
        } catch (EmptyResultDataAccessException e) {
            logger.info(".......databse no record .........");
            return null;
        }
    }

    /**
     * @Description 查詢Map List
     * @author HeLiu
     * @date 2018/7/30 17:33
     */
    public List<Map<String, Object>> queryForList(String sqlId, Object param) {
        Map paramMap = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        String sql = xmlParse.getSql(sqlId);
        sql = freeMakerParser.process(sql, paramMap);

        try {
            return jdbcTemplate.queryForList(sql, paramMap);
        } catch (EmptyResultDataAccessException e) {
            logger.info(".......databse no record .........");
            return null;
        }
    }

    /**
     * @Description 查詢多個物件
     * @author HeLiu
     * @date 2018/7/30 17:38
     */
    public <T> List<T> queryForList(String sqlId, Class<T> clazz) {
        return queryForList(sqlId, new HashMap<>(), clazz);
    }

    /**
     * @Description 儲存資料,並獲得主鍵id
     * @author HeLiu
     * @date 2018/7/30 17:57
     */
    public int insertAndGetId(String sqlId, Object param) {
        Map paramMap = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        String sql = xmlParse.getSql(sqlId);
        sql = freeMakerParser.process(sql, paramMap);

        MapSqlParameterSource parameters = new MapSqlParameterSource(paramMap);
        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(sql, parameters, keyHolder, new String[]{"ID"});
        return keyHolder.getKey().intValue();

    }

    /**
     * @Description 批量儲存,並獲得主鍵id
     * @author HeLiu
     * @date 2018/7/30 17:42
     */
    public List<Integer> batchInsertAndGetId(String sqlId, Object param) {
        Map paramMap = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        String sql = xmlParse.getSql(sqlId);
        sql = freeMakerParser.process(sql, paramMap);

        MapSqlParameterSource parameters = new MapSqlParameterSource(paramMap);
        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(sql, parameters, keyHolder, new String[]{"ID"});

        List<Integer> ids = new ArrayList<Integer>();
        for (Map<String, Object> map : keyHolder.getKeyList()) {
            Integer id = MapUtils.getInteger(map, "GENERATED_KEY");
            ids.add(id);
        }
        return ids;
    }

    /**
     * @Description 執行資料庫操作,更新、刪除、儲存,返回執行成功條數
     * @author HeLiu
     * @date 2018/7/30 18:03
     */
    public int excute(String sqlId, Object param) {
        Map paramMap = JSONObject.parseObject(JSONObject.toJSONString(param), Map.class);
        String sql = xmlParse.getSql(sqlId);
        sql = freeMakerParser.process(sql, paramMap);

        return jdbcTemplate.update(sql, paramMap);
    }
}

sql解析需要的兩個檔案: 

  • XmlParse.java
package com.heliu.base;

import org.apache.commons.lang3.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author HeLiu
 * @Description xml解析
 * @date 2018/7/30 10:24
 */
public class XmlParse implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(XmlParse.class);

    private Resource[] resources;

    public Resource[] getResources() {
        return resources;
    }

    public void setResources(Resource[] resources) {
        this.resources = resources;
    }

    public XmlParse(Resource[] resources) {
        this.resources = resources;
    }

    public XmlParse() {
    }

    private static final Map<String, String> sqlMap = new ConcurrentHashMap<String, String>();

    public Map<String, String> initSqlMap() {
        try {
            if (null == resources || resources.length <= 0) {
                logger.error("沒有配置sql檔案");
                return sqlMap;
            }
            int count = 0;
            for (Resource resource : resources) {
                logger.info("sqlmap 檔案url ......{}", resource.getURL());
                SAXBuilder builder = new SAXBuilder();
                Document doc = builder.build(resource.getInputStream());
                Element foo = doc.getRootElement();

                String namespace = foo.getAttributeValue("namespace");
                if (StringUtils.isBlank(namespace)) {
                    logger.error("sql的xml檔案中,根節點沒有配置namespace屬性");
                    return sqlMap;
                }
                List<Element> childrens = foo.getChildren();
                for (Element ele : childrens) {
                    String id = ele.getAttributeValue("id");
                    if (StringUtils.isBlank(id)) {
                        logger.error("sql的xml檔案中,沒有配置sql的 id屬性");
                        return sqlMap;
                    }
                    String sqlContent = ele.getValue();
                    count++;
                    sqlMap.put(namespace + "." + id, sqlContent);
                }

            }
            logger.info("sql 載入完成!共{}條", count);
        } catch (Exception e) {
            logger.error("解析sqlmap 異常", e);
        }
        return sqlMap;
    }

    public static String getSql(String sqlId) {
        return sqlMap.get(sqlId);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initSqlMap();
    }
}
  •  FreeMakerParser.java
package com.heliu.util;

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author HeLiu
 * @Description freeMaker解析
 * @date 2018/10/22 10:29
 */
public class FreeMakerParser {

    private static Log logger = LogFactory.getLog(FreeMakerParser.class);
    private static final String DEFAULT_TEMPLATE_KEY = "default_template_key";
    private static final String DEFAULT_TEMPLATE_EXPRESSION = "default_template_expression";
    private static final Configuration CONFIGURER = new Configuration();

    static {
        CONFIGURER.setClassicCompatible(true);
    }

    /**
     * 配置SQL表示式快取
     */
    private static Map<String, Template> templateCache = new HashMap<String, Template>();
    /**
     * 分庫表示式快取
     */
    private static Map<String, Template> expressionCache = new HashMap<String, Template>();

    public static String process(String expression, Map<String, Object> root) {
        StringReader reader = null;
        StringWriter out = null;
        Template template = null;
        try {
            if (expressionCache.get(expression) != null) {
                template = expressionCache.get(expression);
            }
            if (template == null) {
                template = createTemplate(DEFAULT_TEMPLATE_EXPRESSION, new StringReader(expression));
                expressionCache.put(expression, template);
            }
            out = new StringWriter();
            template.process(root, out);
            return out.toString();
        } catch (Exception e) {
            logger.error("freemark解析sql 異常", e);
        } finally {
            if (reader != null) {
                reader.close();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                return null;
            }
        }
        return null;
    }

    private static Template createTemplate(String templateKey, Reader reader) throws IOException {
        Template template = new Template(DEFAULT_TEMPLATE_KEY, reader, CONFIGURER);
        template.setNumberFormat("#");
        return template;
    }

    public static String process(Map<String, Object> root, String sql, String sqlId) {
        StringReader reader = null;
        StringWriter out = null;
        Template template = null;
        try {
            if (templateCache.get(sqlId) != null) {
                template = templateCache.get(sqlId);
            }
            if (template == null) {
                reader = new StringReader(sql);
                template = createTemplate(DEFAULT_TEMPLATE_KEY, reader);
                templateCache.put(sqlId, template);
            }
            out = new StringWriter();
            template.process(root, out);
            return out.toString();
        } catch (Exception e) {
            logger.error("freemark解析sql 異常", e);
        } finally {
            if (reader != null) {
                reader.close();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                return null;
            }
        }
        return null;
    }


    /**
     * 模板檔案解析
     *
     * @param paramMap            引數
     * @param resultFileWriter   結果檔案寫入器
     * @param templateFileReader 模板檔案讀取器
     * @author HeLiu
     * @date 2018/10/22 10:29
     */
    public static void process(Map<String, Object> paramMap, Writer resultFileWriter,
                               Reader templateFileReader) {
        CONFIGURER.setDefaultEncoding("UTF-8");// 設定預設編碼方式
        try {

            Template template = createTemplate(DEFAULT_TEMPLATE_EXPRESSION, templateFileReader);
            template.process(paramMap, resultFileWriter);
            logger.info(".............freemark檔案解析完成..........");
        } catch (Exception e) {
            logger.error("freemark 解析異常", e);
            e.printStackTrace();
        }
    }
}

Dao層寫法:

@Repository
public class IndexDao {

    private static final Logger logger = LoggerFactory.getLogger(IndexDao.class);

    @Autowired
    private DaoClient daoClient;

    private final String NAME_SPACE = "student.";  //對應sql檔案namespace
}

sql檔案寫法: 

<?xml version="1.0" encoding="UTF-8"?>
<sqls namespace="student">

    <sqlTemplate id="queryStudent">  //此處id對應方法呼叫的sql
        <![CDATA[
             //這裡面編寫sql語句
        ]]>
    </sqlTemplate>

</sqls>

 注意:

  1. 需要的jar依賴,根據需求匯入,這裡就不詳細講解了
  2. 配置資料來源的時候name要注意,class引用路徑不要引用錯誤,都要根據實際情況修改;
  3. sql:sqlmaps/sqlMap_*.xml;意思就是放在sqlmaps目錄下以sqlMap_*.xml格式命名。
  4. 啟動沒有報錯,就可以利用封裝對資料庫進行一下操作測試是否成功。
  5. String sqlId = NAME_SPACE+"自定義命名"。
  6. 相關配置按照實際情況修改