java文件配置MySQL

MybatisConfig.java文件

  1 import com.alibaba.druid.pool.DruidDataSource;
  2 import com.xman.common.mybatis.SqlMonitorManager;
  3 import org.apache.ibatis.plugin.Interceptor;
  4 import org.apache.ibatis.session.SqlSessionFactory;
  5 import org.mybatis.spring.SqlSessionFactoryBean;
  6 import org.mybatis.spring.annotation.MapperScan;
  7 import org.slf4j.Logger;
  8 import org.slf4j.LoggerFactory;
  9 import org.springframework.beans.factory.annotation.Value;
 10 import org.springframework.context.annotation.Bean;
 11 import org.springframework.context.annotation.Configuration;
 12 import org.springframework.core.env.StandardEnvironment;
 13 import org.springframework.core.io.ClassPathResource;
 14 import org.springframework.core.io.Resource;
 15 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 16 import org.springframework.core.io.support.ResourcePatternResolver;
 17 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 18 import org.springframework.util.ClassUtils;
 19 
 20 import javax.sql.DataSource;
 21 import java.io.IOException;
 22 import java.util.Properties;
 23 
 24 /**
 25  * Created by chai on 2017/10/11.
 26  */
 27 @Configuration  //用作配置信息
 28 @MapperScan(basePackages = "com.xman.rainbow.dao")
 29 public class MybatisConfig {
 30 
 31     private static Logger logger = LoggerFactory.getLogger(MybatisConfig.class);
 32 
 33     @Value("${datasource.driverClass}")
 34     private String jdbcDriver;
 35 
 36     @Value("${datasource.username}")
 37     private String username;
 38 
 39     @Value("${datasource.password}")
 40     private String password;
 41 
 42     @Value("${datasource.jdbcUrl}")
 43     private String jdbcUrl;
 44 
 45     @Value("${datasource.maxIdle}")
 46     private String maxIdle;
 47 
 48     //最小、最大
 49     @Value("${datasource.minIdle}")
 50     private String minIdle;
 51     @Value("${datasource.maxActive}")
 52     private int maxActive;
 53 
 54     @Value("${datasource.maxWait}")
 55     private String maxWait;     //配置获取连接等待超时的时间
 56 
 57     @Value("${datasource.validationQuery}")
 58     private String validationQuery;
 59 
 60     @Value("${datasource.testBorrow}")
 61     private boolean testOnBorrow;
 62 
 63     @Value("${datasource.testOnReturn}")
 64     private boolean testOnReturn;
 65 
 66     @Value("${datasource.testWhileIdle}")
 67     private boolean testWhileIdle;
 68 
 69     @Value("${datasource.timeBetweenEvictionRunsMills}")
 70     private long timeBetweenEvictionRunsMills;  //配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
 71 
 72     @Value("${datasource.minEvictableIdleTimeMillis}")
 73     private long minEvictableTimeMills; //配置一个连接在池中最小生存的时间,单位是毫秒
 74 
 75     @Bean   //为Spring容器所管理
 76     public DataSource dataSource() {
 77         DruidDataSource dataSource = new DruidDataSource();
 78         dataSource.setDriverClassName(this.jdbcDriver);
 79         dataSource.setUsername(this.username);
 80         dataSource.setPassword(this.password);
 81         dataSource.setUrl(this.jdbcUrl);
 82         dataSource.setMaxActive(this.maxActive);
 83         dataSource.setValidationQuery(this.validationQuery);
 84         dataSource.setTestOnBorrow(this.testOnBorrow);
 85         dataSource.setTestOnReturn(this.testOnReturn);
 86         dataSource.setTestWhileIdle(this.testWhileIdle);
 87         dataSource.setTimeBetweenConnectErrorMillis(this.timeBetweenEvictionRunsMills);
 88         dataSource.setMinEvictableIdleTimeMillis(minEvictableTimeMills);
 89         return dataSource;
 90     }
 91 
 92     @Bean
 93     public SqlSessionFactory sqlSessionFactory() throws Exception {
 94         logger.debug("start sqlSessionFactory");
 95         final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
 96 //        设置datasource
 97         sqlSessionFactory.setDataSource(dataSource());
 98 //        设置mybatis configuration 扫描路径
 99         sqlSessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
100         sqlSessionFactory.setFailFast(true);
101         //自动扫描entity目录
102         sqlSessionFactory.setMapperLocations(getResource("mappers", "**/*.xml"));
103         SqlMonitorManager sqlMonitorManager = new SqlMonitorManager();
104         Properties properties = new Properties();
105         properties.setProperty("show_sql", "true");
106         sqlMonitorManager.setProperties(properties);
107 
108         PageInterceptor pageInterceptor = new PageInterceptor();
109         Properties property = new Properties();
110         properties.setProperty("databaseType", "mysql");
111         pageInterceptor.setProperties(property);
112         sqlSessionFactory.setPlugins(new Interceptor[]{sqlMonitorManager, pageInterceptor});
113         return sqlSessionFactory.getObject();
114     }
115 
116     private Resource[] getResource(String basePackage, String pattern) throws IOException {
117         String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
118                 + ClassUtils.convertClassNameToResourcePath(new StandardEnvironment()
119                 .resolveRequiredPlaceholders(basePackage)) + "/" + pattern;
120         Resource[] resources = new PathMatchingResourcePatternResolver().getResources(packageSearchPath);
121         return resources;
122     }
123 
124     /**
125      * 配置事务管理组件
126      * @return
127      */
128     @Bean
129     public DataSourceTransactionManager transactionManager() {
130         logger.debug("start transactionManager");
131         return new DataSourceTransactionManager(dataSource());
132     }
133 }

mybatis-config.xml

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 3 <configuration>
 4     <settings>
 5         <setting name="cacheEnabled" value="true"/>
 6         <setting name="lazyLoadingEnabled" value="true"/>
 7         <setting name="aggressiveLazyLoading" value="true"/>
 8         <setting name="useGeneratedKeys" value="true"/>
 9         <setting name="defaultExecutorType" value="SIMPLE"/>
10         <setting name="defaultStatementTimeout" value="10"/>
11     </settings>
12 </configuration>
View Code

PageInterceptor.java

  1 import com.xman.rainbow.car.common.Page;
  2 import com.xman.rainbow.car.common.Pagination;
  3 import org.apache.ibatis.executor.parameter.ParameterHandler;
  4 import org.apache.ibatis.executor.statement.RoutingStatementHandler;
  5 import org.apache.ibatis.executor.statement.StatementHandler;
  6 import org.apache.ibatis.mapping.BoundSql;
  7 import org.apache.ibatis.mapping.MappedStatement;
  8 import org.apache.ibatis.mapping.ParameterMapping;
  9 import org.apache.ibatis.plugin.*;
 10 import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
 11 
 12 import java.lang.reflect.Field;
 13 import java.sql.Connection;
 14 import java.sql.PreparedStatement;
 15 import java.sql.ResultSet;
 16 import java.sql.SQLException;
 17 import java.util.List;
 18 import java.util.Map;
 19 import java.util.Properties;
 20 
 21 /**
 22  * 分页拦截器,用于拦截需要进行分页查询的操作,然后对其进行分页处理。 利用拦截器实现Mybatis分页的原理:
 23  * 要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前就会产生一个包含Sql语句的Statement对象,而且对应的Sql语句
 24  * 是在Statement之前产生的,所以我们就可以在它生成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的
 25  * prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用
 26  * StatementHandler对象的prepare方法,即调用invocation.proceed()。
 27  * 对于分页而言,在拦截器里面我们还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设
 28  * 置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。
 29  *
 30  */
 31 @Intercepts({ @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }) })
 32 public class PageInterceptor implements Interceptor {
 33 
 34     private String databaseType;// 数据库类型,不同的数据库有不同的分页方法
 35 
 36     /**
 37      * 拦截后要执行的方法
 38      */
 39     public Object intercept(Invocation invocation) throws Throwable {
 40         // 对于StatementHandler其实只有两个实现类,一个是RoutingStatementHandler,另一个是抽象类BaseStatementHandler,
 41         // BaseStatementHandler有三个子类,分别是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler,
 42         // SimpleStatementHandler是用于处理Statement的,PreparedStatementHandler是处理PreparedStatement的,而CallableStatementHandler是
 43         // 处理CallableStatement的。Mybatis在进行Sql语句处理的时候都是建立的RoutingStatementHandler,而在RoutingStatementHandler里面拥有一个
 44         // StatementHandler类型的delegate属性,RoutingStatementHandler会依据Statement的不同建立对应的BaseStatementHandler,即SimpleStatementHandler、
 45         // PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。
 46         // 我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截StatementHandler接口的prepare方法,又因为Mybatis只有在建立RoutingStatementHandler的时候
 47         // 是通过Interceptor的plugin方法进行包裹的,所以我们这里拦截到的目标对象肯定是RoutingStatementHandler对象。
 48         RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
 49         // 通过反射获取到当前RoutingStatementHandler对象的delegate属性
 50         StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
 51         // 获取到当前StatementHandler的 boundSql,这里不管是调用handler.getBoundSql()还是直接调用delegate.getBoundSql()结果是一样的,因为之前已经说过了
 52         // RoutingStatementHandler实现的所有StatementHandler接口方法里面都是调用的delegate对应的方法。
 53         BoundSql boundSql = delegate.getBoundSql();
 54         // 拿到当前绑定Sql的参数对象,就是我们在调用对应的Mapper映射语句时所传入的参数对象
 55         Object paramObj = boundSql.getParameterObject();
 56 
 57         // 判断参数里是否有page对象
 58         Pagination page = null;
 59         if (paramObj instanceof Pagination) {
 60             page = (Pagination) paramObj;
 61         } else if (paramObj instanceof Map) {
 62             for (Object arg : ((Map) paramObj).values()) {
 63                 if (arg instanceof Page<?>) {
 64                     page = (Pagination) arg;
 65                     break;
 66                 }
 67             }
 68         }
 69 
 70         // 这里我们简单的通过传入的参数含有Pagination对象就认定它是需要进行分页操作的。
 71         if (page != null) {
 72             // 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
 73             MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
 74             // 拦截到的prepare方法参数是一个Connection对象
 75             Connection connection = (Connection) invocation.getArgs()[0];
 76             // 获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
 77             String sql = boundSql.getSql();
 78             // 给当前的page参数对象设置总记录数
 79             if (page.getTotalCount() < 0) { // 如果总数为负数表需要设置
 80                 this.setTotalRecord(paramObj, mappedStatement, connection, page);
 81             }
 82             // 获取分页Sql语句
 83             String pageSql = this.getPageSql(page, sql);
 84             // 利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
 85             ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
 86         }
 87         return invocation.proceed();
 88     }
 89 
 90     /**
 91      * 拦截器对应的封装原始对象的方法
 92      */
 93     public Object plugin(Object target) {
 94         return Plugin.wrap(target, this);
 95     }
 96 
 97     /**
 98      * 设置注册拦截器时设定的属性
 99      */
100     public void setProperties(Properties properties) {
101         this.databaseType = properties.getProperty("databaseType");
102     }
103 
104     /**
105      * 根据page对象获取对应的分页查询Sql语句,这里只做了两种数据库类型,Mysql和Oracle 其它的数据库都 没有进行分页
106      *
107      * @param page 分页对象
108      * @param sql 原sql语句
109      * @return
110      */
111     private String getPageSql(Pagination page, String sql) {
112         StringBuffer sqlBuffer = new StringBuffer(sql);
113         if ("mysql".equalsIgnoreCase(databaseType)) {
114             return getMysqlPageSql(page, sqlBuffer);
115         } else if ("oracle".equalsIgnoreCase(databaseType)) {
116             return getOraclePageSql(page, sqlBuffer);
117         }
118         return getMysqlPageSql(page, sqlBuffer);
119     }
120 
121     /**
122      * 获取Mysql数据库的分页查询语句
123      *
124      * @param page 分页对象
125      * @param sqlBuffer 包含原sql语句的StringBuffer对象
126      * @return Mysql数据库分页语句
127      */
128     private String getMysqlPageSql(Pagination page, StringBuffer sqlBuffer) {
129         // 计算第一条记录的位置,Mysql中记录的位置是从0开始的。
130         int offset = (page.getPageNo() - 1) * page.getPageCount();
131         sqlBuffer.append(" limit ").append(offset).append(",").append(page.getPageCount());
132         return sqlBuffer.toString();
133     }
134 
135     /**
136      * 获取Oracle数据库的分页查询语句
137      *
138      * @param page 分页对象
139      * @param sqlBuffer 包含原sql语句的StringBuffer对象
140      * @return Oracle数据库的分页查询语句
141      */
142     private String getOraclePageSql(Pagination page, StringBuffer sqlBuffer) {
143         // 计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的
144         int offset = (page.getPageNo() - 1) * page.getPageCount() + 1;
145         sqlBuffer.insert(0, "select u.*, rownum _rownum from (").append(") u where rownum < ")
146                 .append(offset + page.getPageCount());
147         sqlBuffer.insert(0, "select * from (").append(") where _rownum >= ").append(offset);
148         // 上面的Sql语句拼接之后大概是这个样子:
149         // select * from (select u.*, rownum r from (select * from t_user) u where rownum < 31) where r >= 16
150         return sqlBuffer.toString();
151     }
152 
153     /**
154      * 给当前的参数对象page设置总记录数
155      *
156      * @param obj Mapper映射语句对应的参数对象
157      * @param mappedStatement Mapper映射语句
158      * @param connection 当前的数据库连接
159      */
160     private void setTotalRecord(Object obj, MappedStatement mappedStatement, Connection connection, Pagination page) {
161         // 获取对应的BoundSql,这个BoundSql其实跟我们利用StatementHandler获取到的BoundSql是同一个对象。
162         // delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。
163         BoundSql boundSql = mappedStatement.getBoundSql(obj);
164         // 获取到我们自己写在Mapper映射语句中对应的Sql语句
165         String sql = boundSql.getSql();
166         // 通过查询Sql语句获取到对应的计算总记录数的sql语句
167         String countSql = this.getCountSql(sql);
168         // 通过BoundSql获取对应的参数映射
169         List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
170         // 利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。
171         BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, obj);
172         // 在原boundSQL中存在additionalParameters等参数,new出来的sql可能没有这些参数,会造成生成sql是报错,所以设置进来
173         // 还有一种方法即就用原来的BoundSql, 改掉里面的sql, 用完后再改回来即可
174         ReflectUtil.setFieldValue(countBoundSql, "additionalParameters", ReflectUtil.getFieldValue(boundSql, "additionalParameters"));
175         ReflectUtil.setFieldValue(countBoundSql, "metaParameters", ReflectUtil.getFieldValue(boundSql, "metaParameters"));
176 
177         // 通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象
178         ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, obj, countBoundSql);
179         // 通过connection建立一个countSql对应的PreparedStatement对象。
180         PreparedStatement pstmt = null;
181         ResultSet rs = null;
182         try {
183             pstmt = connection.prepareStatement(countSql);
184             // 通过parameterHandler给PreparedStatement对象设置参数
185             parameterHandler.setParameters(pstmt);
186             // 之后就是执行获取总记录数的Sql语句和获取结果了。
187             rs = pstmt.executeQuery();
188             if (rs.next()) {
189                 int totalRecord = rs.getInt(1);
190                 // 给当前的参数page对象设置总记录数
191                 page.setTotalCount(totalRecord);
192             }
193         } catch (SQLException e) {
194             e.printStackTrace();
195         } finally {
196             try {
197                 if (rs != null)
198                     rs.close();
199                 if (pstmt != null)
200                     pstmt.close();
201             } catch (SQLException e) {
202                 e.printStackTrace();
203             }
204         }
205     }
206 
207     /**
208      * 根据原Sql语句获取对应的查询总记录数的Sql语句
209      *
210      * @param sql
211      * @return
212      */
213     private String getCountSql(String sql) {
214         return "select count(1) from (" + sql + ") _tmp";
215     }
216 
217     /**
218      * 利用反射进行操作的一个工具类
219      *
220      */
221     private static class ReflectUtil {
222         /**
223          * 利用反射获取指定对象的指定属性
224          *
225          * @param obj 目标对象
226          * @param fieldName 目标属性
227          * @return 目标属性的值
228          */
229         public static Object getFieldValue(Object obj, String fieldName) {
230             Object result = null;
231             Field field = ReflectUtil.getField(obj, fieldName);
232             if (field != null) {
233                 field.setAccessible(true);
234                 try {
235                     result = field.get(obj);
236                 } catch (IllegalArgumentException e) {
237                     // TODO Auto-generated catch block
238                     e.printStackTrace();
239                 } catch (IllegalAccessException e) {
240                     // TODO Auto-generated catch block
241                     e.printStackTrace();
242                 }
243             }
244             return result;
245         }
246 
247         /**
248          * 利用反射获取指定对象里面的指定属性
249          *
250          * @param obj 目标对象
251          * @param fieldName 目标属性
252          * @return 目标字段
253          */
254         private static Field getField(Object obj, String fieldName) {
255             Field field = null;
256             for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
257                 try {
258                     field = clazz.getDeclaredField(fieldName);
259                     break;
260                 } catch (NoSuchFieldException e) {
261                     // 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
262                 }
263             }
264             return field;
265         }
266 
267         /**
268          * 利用反射设置指定对象的指定属性为指定的值
269          *
270          * @param obj 目标对象
271          * @param fieldName 目标属性
272          * @param fieldValue 目标值
273          */
274 /*        public static void setFieldValue(Object obj, String fieldName, String fieldValue) {
275             Field field = ReflectUtil.getField(obj, fieldName);
276             if (field != null) {
277                 try {
278                     field.setAccessible(true);
279                     field.set(obj, fieldValue);
280                 } catch (IllegalArgumentException e) {
281                     // TODO Auto-generated catch block
282                     e.printStackTrace();
283                 } catch (IllegalAccessException e) {
284                     // TODO Auto-generated catch block
285                     e.printStackTrace();
286                 }
287             }
288         }*/
289         /**
290          * 利用反射设置指定对象的指定属性为指定的值
291          *
292          * @param obj 目标对象
293          * @param fieldName 目标属性
294          * @param fieldValue 目标值
295          */
296         public static void setFieldValue(Object obj, String fieldName, Object fieldValue) {
297             Field field = ReflectUtil.getField(obj, fieldName);
298             if (field != null) {
299                 try {
300                     field.setAccessible(true);
301                     field.set(obj, fieldValue);
302                 } catch (IllegalArgumentException e) {
303                     // TODO Auto-generated catch block
304                     e.printStackTrace();
305                 } catch (IllegalAccessException e) {
306                     // TODO Auto-generated catch block
307                     e.printStackTrace();
308                 }
309             }
310         }
311     }
312 
313 }

application.properties文件

 1 #### 测试环境
 2 
 3 # datasource
 4 # 驱动配置信息
 5 datasource.driverClass=com.mysql.jdbc.Driver
 6 datasource.username=root
 7 datasource.password=123456
 8 datasource.jdbcUrl=localhost
 9 #连接池的配置信息
10 datasource.maxIdle=20
11 datasource.minIdle=1
12 datasource.maxWait=60000
13 datasource.validationQuery=: /* ping */ select 1
14 datasource.maxActive=3
15 datasource.testBorrow=false
16 datasource.testOnReturn=false
17 datasource.testWhileIdle=true
18 datasource.timeBetweenEvictionRunsMills=60000
19 datasource.minEvictableIdleTimeMillis=60000
原文地址:https://www.cnblogs.com/xinlichai0813/p/7654560.html