实现目标:
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);
}
}
测试前数据库
测试后数据库