搭建通用的SSM框架 (二) 使用druid管理数据源,以及使用Mybatis操作数据库

github的backend分支

实现目标:

                1)使用druid管理数据库的数据源连接

                2) 使用Mybatis的OGNL表达式,动态构造数据简单的增删改查操作

                3) 实现log4j日志的打印

                4) 添加Spring测试案例,实现数据库的增删改查操作

                5) 使用Spring注解方式实现事务管理

2.1) 增加properties文件

    2.1.1) log4j.properties文件

#定义LOG输出级别
log4j.rootLogger=INFO,Console,File  
#定义日志输出目的地为控制台
log4j.appender.Console=org.apache.log4j.ConsoleAppender  
log4j.appender.Console.Target=System.out  
#可以灵活地指定日志输出格式,下面一行是指定具体的格式
log4j.appender.Console.layout = org.apache.log4j.PatternLayout  
log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n  

#文件大小到达指定尺寸的时候产生一个新的文件
log4j.appender.File = org.apache.log4j.RollingFileAppender  
#指定输出目录
log4j.appender.File.File = F:/logs/common-ssm.log  
#定义文件最大大小
log4j.appender.File.MaxFileSize = 10MB  
# 输出所以日志,如果换成DEBUG表示输出DEBUG以上级别日志
log4j.appender.File.Threshold = ALL  
log4j.appender.File.layout = org.apache.log4j.PatternLayout  
log4j.appender.File.layout.ConversionPattern =[%p] [%d{yyyy-MM-dd HH\:mm\:ss}][%c]%m%n

 2.1.2) druid.propreties

##druid数据库连接池配置##
# MySql数据库连接信息
datasource.druid.url=jdbc:mysql://192.168.10.123:3306/common_ssm?useUnicode=true&characterEncoding=UTF-8&useSSL=false
# 根据url自动识别 这一项可配可不配,
# 如果不配置druid会根据url自动识别dbType,
# 然后选择相应的driverClassName(建议配置下)
datasource.druid.driverClassName=com.mysql.jdbc.Driver
datasource.druid.userName=root
datasource.druid.password=MaryF6650

# 初始化时建立物理连接的个数
# 初始化发生在显示调用init方法,或者第一次getConnection时
datasource.druid.initialSize=5
# 最小连接池数量
datasource.druid.minIdle=5
# 最大连接池数量
datasource.druid.maxActive=25
# 获取最大连接时最大等待时间,单位时毫秒
# 配置了maxWait之后,缺省启用公平锁,并发效率会有所下降
# 如果需要可以通过配置useUnfairLock属性为true使用非公平锁
datasource.druid.maxWait=5000
# 是否缓存preparedStatement,也就是PSCache
# PSCache对支持游标的数据库性能提升巨大,比如说oracle.
# 在mysql下建议关闭
datasource.druid.poolPreparedStatements=false
# 当poolPreparedStatements配置为true时,才可以使用
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
# 在Druid中,不会存在Oracle下PSCache占用内存过多的问题
# 可以把这个数值配置大一些,比如说100
#datasource.druid.maxOpenPreparedStatements=100
#用来检测连接是否有效的sql,要求是一个查询语句
# 如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
datasource.druid.validationQuery = SELECT 'x'
# 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
# 开发时使用,生产环境建议关闭
# 默认值为true
datasource.druid.testOnBorrow=true
# 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
# 默认值为false
datasource.druid.testOnReturn=false
#建议配置为true,不影响性能,并且保证安全性.申请连接的时候检测
# 如果空闲时间大于timeBetweenEvictionRunsMillis
# 执行validationQuery检测连接是否有效
# 默认值为false
datasource.druid.testWhileIdle=true
# 1)Destroy线程会检测连接的间隔时间
# 2)testWhileIdle的判断依据
datasource.druid.timeBetweenEvictionRunsMillis=5000
#一个连接在池中最小生存的时间,单位是毫秒
datasource.druid.minEvictableIdleTimeMillis=300000

# 属性类型是字符串,通过别名的方式配置扩展插件,
# 常用的插件有: 监控统计用的filter:stat日志用的filter:log4j 防御sql注入的filter:wall
# 如果想配置多个filter使用 proxyFilters 属性
# proxyFilters 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
datasource.druid.filters=stat

2.2) 创建一个Spring管理容器类,管理项目中带注解的类,对其进行初始化等操作

package com.roger.core.config;

import com.roger.core.constant.SystemConstant;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * Spring管理容器
 */
@Configuration
@ComponentScan(value = SystemConstant.COMPONENT_SCAN_PACKAGE)
public class SpringConfig {
}

2.3) 根据2.1配置的属性文件创建druid数据源类,并交给Spring容器管理

package com.roger.core.config;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.sql.SQLException;

@Configuration
@PropertySource("classpath:druid.properties")
@Slf4j
public class CommonDataSource {

    @Value("${datasource.druid.driverClassName}")
    private String driveClassName;
    @Value("${datasource.druid.url}")
    private String url;
    @Value("${datasource.druid.userName}")
    private String userName;
    @Value("${datasource.druid.password}")
    private String password;
    @Value("${datasource.druid.initialSize}")
    private int initialSize;
    @Value("${datasource.druid.minIdle}")
    private int minIdle;
    @Value("${datasource.druid.maxActive}")
    private int maxActive;
    @Value("${datasource.druid.maxWait}")
    private long maxWait;
    @Value("${datasource.druid.poolPreparedStatements}")
    private boolean poolPreparedStatements;
    @Value("${datasource.druid.validationQuery}")
    private String validationQuery;
    @Value("${datasource.druid.testOnBorrow}")
    private boolean testOnBorrow;
    @Value("${datasource.druid.testOnReturn}")
    private boolean testOnReturn;
    @Value("${datasource.druid.testWhileIdle}")
    private boolean testWhileIdle;
    @Value("${datasource.druid.timeBetweenEvictionRunsMillis}")
    private long timeBetweenEvictionRunsMillis;
    @Value("${datasource.druid.minEvictableIdleTimeMillis}")
    private long minEvictableIdleTimeMillis;
    @Value("${datasource.druid.filters}")
    private String filters;

    @Bean
    public DruidDataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new com.alibaba.druid.pool.DruidDataSource();
        druidDataSource.setDriverClassName(driveClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(userName);
        druidDataSource.setPassword(password);

        druidDataSource.setInitialSize(initialSize);

        druidDataSource.setMinIdle(minIdle);
        druidDataSource.setMaxActive(maxActive);
        druidDataSource.setMaxWait(maxWait);

        druidDataSource.setPoolPreparedStatements(poolPreparedStatements);

        druidDataSource.setValidationQuery(validationQuery);
        druidDataSource.setTestOnBorrow(testOnBorrow);
        druidDataSource.setTestOnReturn(testOnReturn);
        druidDataSource.setTestWhileIdle(testWhileIdle);
        druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

        druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);

        druidDataSource.setFilters(filters);
        log.info("druid数据源配置完成");
        return druidDataSource;
    }

}

2.4)创建Mybatis管理数据源的工厂类,并把2.3创建的数据源注入

package com.roger.core.config;

import com.roger.core.constant.SystemConstant;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;
import java.io.IOException;

@Configuration
@Import(CommonDataSource.class)
@MapperScan(value = SystemConstant.MAPPER_SCAN_BASE_PACKAGE)
public class MyBatis {


    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();

        sqlSessionFactoryBean.setDataSource(dataSource);

        //打印SQL语句到控制台
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLogImpl(StdOutImpl.class);
        sqlSessionFactoryBean.setConfiguration(configuration);

        //把相关的Mapper配置文件加入导入到SqlSessionFactory中
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources(SystemConstant.MAPPER_XML_PATH));

        sqlSessionFactoryBean.setTypeAliasesPackage(SystemConstant.TYPE_ALIASES_PACKAGE);

        return sqlSessionFactoryBean;
    }

}

2.5)创建Spring事务管理器,实现通过注解方式控制数据库的事务

package com.roger.core.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@Import(CommonDataSource.class)
@EnableTransactionManagement
public class SpringTX {

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

}

2.6)创建通用的GeneralMapper类,通过OGNL表达式实现针对任意表进行增删改查操作

package com.roger.core.mapper;

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

public interface GeneralMapper {

    Map<String,Object> selectByPrimaryKey(Map<String, Object> param);

    /**
     * 高级查询:参数可以动态设置
     * @param param
     * @return
     */
    List<Map<String,Object>> selectAdvanced(Map<String, Object> param);

    int insert(Map<String, Object> param);

    int batchInsert(Map<String, Object> param);

    int update(Map<String, Object> param);

    int deleteByPrimaryKey(Map<String, Object> param);

    /**
     * 1.高级删除
     * 2.此操作需要谨慎,如果不传递删除条件,则会把整张表的数据情况
     */
    int deleteAdvanced(Map<String, Object> param);
}

2.7) 根据2.6的创建的类,创建Mybatis的xml配置文件GeneralMapper.xml

<?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="com.roger.core.mapper.GeneralMapper">
    <!-- 根据主键查询数据 -->
    <select id="selectByPrimaryKey" parameterType="map" resultType="hashMap">
          select
              <choose>
                  <when test="columnNames != null and columnNames.size()>0">
                      <foreach item="columnName" index="index" collection="columnNames" separator=",">
                          ${columnName}
                      </foreach>
                  </when>
                  <otherwise>
                      *
                  </otherwise>
              </choose>
          from ${tableName}
          where ${primaryKey} = #{primaryValue}
    </select>
    <!-- 根据动态条件查询数据 -->
    <select id="selectAdvanced" parameterType="map" resultType="hashmap">
        select
            <choose>
                <when test="columnNames != null and columnNames.size()>0">
                    <foreach item="columnName" index="index" collection="columnNames" separator=",">
                        ${columnName}
                    </foreach>
                </when>
                <otherwise>
                    *
                </otherwise>
            </choose>
        from ${tableName}
        <if test="conditionExp != null">
            <where>
                ${conditionExp}
            </where>
        </if>
        <if test="orderExp != null">
            order by ${orderExp}
        </if>
        <if test="page != null">
            limit #{page.startRowNo},#{page.pageSize}
        </if>
    </select>
    <!-- 插入单个对象 -->
    <insert id="insert" parameterType="map">
        insert into ${tableName}
        <foreach collection="sqlColumns" item="sqlColumn" open="(" separator="," close=")">
            ${sqlColumn.columnName}
        </foreach>
        values
        <foreach collection="sqlColumns" item="sqlColumn" open="(" separator="," close=")">
            <if test="sqlColumn.jdbcType == null">
                #{sqlColumn.columnValue}
            </if>
            <if test="sqlColumn.jdbcType != null">
                #{sqlColumn.columnValue,jdbcType=${sqlColumn.jdbcType}}
            </if>
        </foreach>
    </insert>

    <!-- 批量插入 -->
    <insert id="batchInsert" parameterType="map">
        insert into ${tableName}
        <foreach collection="columnNames" item="columnName" index="index" open="(" separator="," close=")">
            ${columnName}
        </foreach>
        <foreach collection="dataList" item="sqlColumns" index="rowIndex" separator="union all">
            (select
              <foreach collection="sqlColumns" index="columnIndex" item="sqlColumn" separator=",">
                  <if test="sqlColumn.jdbcType == null">
                      #{sqlColumn.columnValue}
                  </if>
                  <if test="sqlColumn.jdbcType != null">
                      #{sqlColumn.columnValue,jdbcType=${sqlColumn.jdbcType}}
                  </if>
              </foreach>
            from dual)
        </foreach>
    </insert>

    <update id="update" parameterType="map">
          update ${tableName}
          <set>
              <foreach collection="sqlColumns" item="sqlColumn" index="index" separator=",">
                  <if test="sqlColumn.columnValue != null">
                      ${sqlColumn.columnName} =
                      <if test="sqlColumn.jdbcType == null">
                          #{sqlColumn.columnValue}
                      </if>
                      <if test="sqlColumn.jdbcType != null">
                          #{sqlColumn.columnValue,jdbcType=${sqlColumn.jdbcType}}
                      </if>
                  </if>
              </foreach>
          </set>
          <if test="conditionExp != null">
            <where>
                ${conditionExp}
            </where>
          </if>
    </update>

    <delete id="deleteByPrimaryKey" parameterType="map">
        delete from ${tableName}
        where ${primaryKey} = #{primaryValue}
    </delete>
    <!--
        1.高级删除
        2.此操作需要谨慎,如果不传递删除条件,则会把整张表的数据情况
    -->
    <delete id="deleteAdvanced" parameterType="map">
        delete from ${tableName}
        <if test="conditionExp != null">
            <where>
                ${conditionExp}
            </where>
        </if>
    </delete>
</mapper>

2.8) 动态实现数据库表数据添加的其他一些类,请参靠源码,在这里不做赘述

2.9) 创建Spring测试案例基类 SpringBaseTestSuit

package com.roger;

import com.roger.core.config.CommonDataSource;
import com.roger.core.config.MyBatis;
import com.roger.core.config.SpringConfig;
import com.roger.core.config.SpringTX;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {CommonDataSource.class, MyBatis.class, SpringTX.class, SpringConfig.class})
public class SpringBaseTestSuit {
}

2.10) 创建GeneralDaoImpl的测试类

package com.roger.core.dao.impl;

import com.roger.SpringBaseTestSuit;
import com.roger.biz.entity.User;
import com.roger.core.constant.OgnlParamConstant;
import com.roger.core.dao.GeneralDao;
import com.roger.core.db.GeneralOgnlParam;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

public class TestGeneralDaoImpl extends SpringBaseTestSuit {

    @Autowired(required = false)
    private GeneralDao crudDao;

    @Test
    public void testInsert() {
        User user = new User();
        user.setUserName("Jackson");
        user.setAge(38);
        user.setPhone("15498756489");
        int count = crudDao.insert(user);
        Assert.assertTrue(count == 1);
    }

}

  测试前数据库

   测试后数据库

猜你喜欢

转载自blog.csdn.net/lihongtai/article/details/87092281