本文主要是介绍MybatisGenerator以及Mybatis配置和抽取反射进行分页查询并批量写入磁盘文件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
由于业务需要,需要运用java来抽取数据库中的几十张表的全表信息,之后进行特定分隔并写入以表名为文件名的文本文件。
1.使用MybatisGenerator进行表和mapper的映射。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!-- 配置生成器 -->
<generatorConfiguration><!-- 加载JDBC配置文件 --><properties resource="config/jdbc.properties"/><!-- 指定数据库驱动的jdbc驱动jar包的位置 --><classPathEntry location="${jdbc.driverLocation}" /><!-- context:生成一组对象的环境 --><context id="MyBatis" targetRuntime="MyBatis3" defaultModelType="flat"><!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; --><property name="beginningDelimiter" value=""/><property name="endingDelimiter" value=""/><!-- 生成的model实现Serializable借口 --><plugin type="org.mybatis.generator.plugins.SerializablePlugin"><!--<property name="suppressJavaInterface" value="true"/>--></plugin><!-- 自动生成equals方法和hashcode方法 -->
<!-- <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>--><!-- 该插件给实体类添加toString() --><plugin type="org.mybatis.generator.plugins.ToStringPlugin"/><!-- <plugin type="org.mybatis.generator.plugins.CaseInsensitiveLikePlugin"/>-->
<!-- <plugin type="org.mybatis.generator.plugins.MapperAnnotationPlugin"/>--><!-- <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin"/>--><!-- 不生成注解 --><commentGenerator><!-- 这个元素用来去除指定生成的注释中是否包含生成的日期 false:表示保护 --><!-- 如果生成日期,会造成即使修改一个字段,整个实体类所有属性都会发生变化,不利于版本控制,所以设置为true --><property name="suppressDate" value="true" /><!-- 是否去除自动生成的注释 true:是 : false:否 --><property name="suppressAllComments" value="true" /></commentGenerator><!--数据库链接URL,用户名、密码 --><jdbcConnection driverClass="${jdbc.driverClassName}"connectionURL="${jdbc.url}"userId="${jdbc.username}"password="${jdbc.password}"></jdbcConnection><!-- java类型处理器 --><javaTypeResolver><property name="forceBigDecimals" value="false" /></javaTypeResolver><!-- java模型创建器targetPackage:生成的类要放的包targetProject:目标项目,指定一个存在的目录下,生成的内容会放到指定目录中--><!-- 生成模型的包名和位置 文件夹自己定义 --><javaModelGenerator targetPackage="com.simon.audit.pojo"targetProject="src/main/java"><!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false --><property name="enableSubPackages" value="false" /><!-- 设置是否在getter方法中,对String类型字段调用trim()方法 --><property name="trimStrings" value="true" /></javaModelGenerator><!-- 生成映射文件的包名和位置 文件夹自己定义--><sqlMapGenerator targetPackage="com.simon.audit.mapper"targetProject="src/main/java"><property name="enableSubPackages" value="false" /></sqlMapGenerator><!-- 生成DAO的包名和位置 文件夹自己定义--><javaClientGenerator type="XMLMAPPER"targetPackage="com.simon.audit.mapper"targetProject="src/main/java"><property name="enableSubPackages" value="false" /></javaClientGenerator><!-- 要生成哪些表 , %表示生成全部的表--><!-- 基表代码表--><table schema="xxxxx" tableName="yyyyy" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table></context>
</generatorConfiguration>
2.在resource目录下新建conf/jdbc.properties
2.将需要生成的表填到<table>标签下,运行Maven下的mybatis-generator即可生成pojo、Mapper.java和Mapper.xml
3.引入Mybatis的Maven依赖并配置Mybatis.xml
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.6</version></dependency><dependency><groupId>com.oracle</groupId><artifactId>ojdbc14</artifactId><version>10.2.0.3.0</version></dependency><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.5</version></dependency>
<?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><!-- 导入数据库相关配置文件(*注意:最多只能有一个) --><properties resource="config/jdbc.properties" /><!-- 全局变量配置 --><settings><!-- 开启驼峰命名法转换 --><setting name="mapUnderscoreToCamelCase" value="true"/><!-- 添加改行配置即可 --><setting name="logImpl" value="LOG4J"/></settings><!-- 不使用权限定名称时配置简称(两种方式,1.直接配置包名称;2.为每一个DO配置简称) --><typeAliases><package name="com.**.pojo"/></typeAliases><!-- 配置数据源环境(可多个,但必须配置default数据源) --><environments default="development"><!-- 第一个数据源 --><environment id="development"><transactionManager type="JDBC"/><dataSource type="com.simon.audit.dataSource.DuridDataSourceFactory">
<!-- <property name="driverClassName" value="${jdbc.driverClassName}"/>--><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><property name="initialSize" value="1"/><property name="maxActive" value="20" /><!-- 配置获取连接等待超时的时间 --><property name="maxWait" value="60000" /></dataSource></environment><!-- 第二个数据源 -->
<!-- <environment id="dataSource2">-->
<!-- <transactionManager type="JDBC" />-->
<!-- <dataSource type="POOLED">-->
<!-- <property name="driver" value="${DS-2.driver}" />-->
<!-- <property name="url" value="${DS-2.url}"/>-->
<!-- <property name="username" value="${DS-2.user}"/>-->
<!-- <property name="password" value="${DS-2.password}"/>-->
<!-- </dataSource>-->
<!-- </environment>--></environments><!-- 将mapper.xml映射到mybatis中(这里的xml文件如果是放在resource目录下面的话就必须老老实实配置每一个xml文件,如果是放在src下面某一个包内,可以用package指定包路径) --><mappers><package name="com.simon.audit.mapper"/></mappers>
</configuration>
4.Mybatis.xml中数据源用的是Druid,配置数据源
public class DuridDataSourceFactory extends UnpooledDataSourceFactory {public DuridDataSourceFactory() {dataSource = new DruidDataSource();}
}
5.配置SqlSessionFactory
public class MyBatisSqlSessionUtil {/*** mybatis相关参数*/private final static String CONFIG_PATH = "config/mybatis.xml";private static InputStream input;private SqlSession sqlSession;private void init(String environment){try {input = Resources.getResourceAsStream(CONFIG_PATH);} catch (IOException e) {e.printStackTrace();}// 通过mybatis配置文件和datasource名称获取sqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input, environment);// 设置自动提交sqlSession = sqlSessionFactory.openSession(true);}/*** 使用默认datasource环境的构造器*/public MyBatisSqlSessionUtil(){init(null);}/*** 使用指定datasource环境的构造器*/public MyBatisSqlSessionUtil(String environment){init(environment);}/*** 创建Mapper实例*/public <T> T getMapper(Class<T> clazz){return sqlSession.getMapper(clazz);}/*** 关闭连接,使用结束后必须执行*/public void close(){if (null != sqlSession) {sqlSession.close();}if (null != input) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}
6.在生成的所有Mapper.xml和Mapper.java中添加公共方法
List<XXXXXX> selectAll(Map<String, Integer> map);int selectCount();
<select id="selectCount" resultType="int">select count(0)from xxxx.XXXXXXX</select><select id="selectAll" parameterType="map" resultMap="BaseResultMap">select<include refid="Base_Column_List"/>from xxxxx.XXXXXXXwhere 1=1<![CDATA[and rownum > #{start}]]><![CDATA[and rownum <= #{end}]]></select>
7.进行反射抽取以及分页查询和批量写入
getResult方法传入需要写入文件的文件名称fileName(表名),和需要反射的xxxMapper.class。进行getClass().getMethods()的方法全量反射。这里反射的是Mapper.java接口中定义的insert、update、select等方法和对应实体类的toString、hashcode、equals等方法。所有不需要用到的方法尽量可以删去,提高效率。
如果匹配"selectCount"就用该method进行invoke,得到该表的count值。
如果count值大于设定值(比如10w或者100w)需要进行分页查询,否则select * 很难查出来,更有可能导致OOM。此时需要重新开启一个SqlSession,如果一直用之前创建的SQLSession不关闭会导致Mybatis一级缓存得不到释放,内存溢出。如果查询一次就关闭也会导致循环条件下Session关闭报错。所有在适当的条件下进行SQLSession的重新创建并对之前创建的对象进行手动垃圾回收System.gc。
在满足大于设定值的情况下,针对Sql进行分页查询。例如每次查询1w条,并将查询结果添加到allList中,allList集合满足10w条记录就往磁盘写一次。磁盘文件文件写满100w条就创建下一个磁盘文件再次进行写入。
public <T, E> List<E> getResult(String fileName,Class<T> t) {MyBatisSqlSessionUtil myBatisSqlSessionUtil = new MyBatisSqlSessionUtil();T myBatisSqlSessionUtilMapper = myBatisSqlSessionUtil.getMapper(t);Method[] methods = myBatisSqlSessionUtilMapper.getClass().getMethods();List<E> list = null;List<Object> allList=new ArrayList<>();int count = 0;boolean endFlag = false;int fileCount = 0;Map<String, Integer> map = new HashMap<>();Method selectCountMethod = null;Method selectAllMethod = null;String nowFileName = fileName;for (Method method : methods) {if ("selectCount".equals(method.getName())) {selectCountMethod = method;}if ("selectAll".equals(method.getName())) {selectAllMethod = method;}}try {count = (int) selectCountMethod.invoke(myBatisSqlSessionUtilMapper);} catch (IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}try {if (count > Constant.PAGE_LINE_COUNT) {MyBatisSqlSessionUtil myBatisSqlSessionUtil1 = myBatisSqlSessionUtil;T tMapper = myBatisSqlSessionUtilMapper;for (int i = 0; i < count; i += Constant.SELECT_EVERY_COUNT) {//重新定义session,,便于关闭Mybatis缓存map.put("start", i);map.put("end", i + Constant.SELECT_EVERY_COUNT);if (i + Constant.SELECT_EVERY_COUNT >= count) {map.put("end", count);endFlag = true;}allList.addAll((List<E>) selectAllMethod.invoke(tMapper, map));if(allList.size() >= Constant.WRITE_TIME_FILE_COUNT || endFlag){if(fileCount >= Constant.WRITE_NEXT_FILE_COUNT){String[] s = nowFileName.split("#");if(s.length == 2){nowFileName =s[0]+"#"+(Integer.valueOf(s[1])+1);MergeFile.createFileNum(Integer.valueOf(s[1])+1);}else {nowFileName = nowFileName+"#"+1;MergeFile.createFileNum(1);}writeResult(nowFileName,allList);fileCount = 0;allList.clear();//关闭session ,清除一级缓存myBatisSqlSessionUtil1.close();//手动进行垃圾回收System.gc();// 因为finalize方法优先级很低,所以暂停0.5秒以等待它Thread.sleep(500);myBatisSqlSessionUtil1 = new MyBatisSqlSessionUtil();tMapper = myBatisSqlSessionUtil1.getMapper(t);}else{//批量写入writeResult(nowFileName,allList);fileCount += Constant.WRITE_TIME_FILE_COUNT;allList.clear();}}}handleFiles(fileName);} else {map.put("start", 0);map.put("end", count);list = (List<E>) selectAllMethod.invoke(myBatisSqlSessionUtilMapper, map);writeResult(fileName, (List<Object>) list);}} catch (IllegalAccessException | InterruptedException | InvocationTargetException ex) {ex.printStackTrace();}myBatisSqlSessionUtil.close();return list;}
8.最后处理文件handleFiles()
遍历生成文件的flieCount数(比如生成了13个带#的临时文件),将infile中的文件内容追加合并到outfile文件中。利用nio的FileChannel管道进行读写最后将所有文件归集到不带#的文件中,即最后需要的文件。切记最后一定要关闭流和手动垃圾回收。否则后面的deleteTempFile方法不能完全删除临时文件。
private void handleFiles(String fileName) {System.out.println("-------------------开始合并文件-------------------");MergeFile.merger(fileName);MergeFile.deleteTempFile(fileName);System.out.println("-------------------完成合并文件-------------------");}public static void merger(String fileName){String infile;String outfile;try {for (int i= fileCount; i > 0; i--) {infile = Constant.PATH + Constant.FILE_NAME_START + Constant.SEPARATION + fileName+"#"+i + Constant.SEPARATION + Constant.FILE_NAME_END + Constant.TXT_SUFFIX;outfile = Constant.PATH + Constant.FILE_NAME_START + Constant.SEPARATION + fileName+"#"+(i-1) + Constant.SEPARATION + Constant.FILE_NAME_END + Constant.TXT_SUFFIX;if(i == 1){outfile = Constant.PATH + Constant.FILE_NAME_START + Constant.SEPARATION + fileName+ Constant.SEPARATION + Constant.FILE_NAME_END + Constant.TXT_SUFFIX;}// 获取源文件和目标文件的输入输出流FileInputStream fin = new FileInputStream(infile);FileOutputStream fout = new FileOutputStream(outfile,true);// 获取输入输出通道FileChannel fcin = fin.getChannel();FileChannel fcout = fout.getChannel();// 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);while (true) {// clear方法重设缓冲区,使它可以接受读入的数据buffer.clear();// 从输入通道中将数据读到缓冲区int r = fcin.read(buffer);// read方法返回读取的字节数,可能为零,如果该通道已到达流的末尾,则返回-1if (r == -1) {break;}// flip方法让缓冲区可以将新读入的数据写入另一个通道buffer.flip();// 从输出通道中将数据写入缓冲区fcout.write(buffer);}fin.close();fout.close();fcin.close();fcout.close();buffer.clear();}} catch (IOException e) {e.printStackTrace();}finally {System.gc();}}public static void deleteTempFile(String fileName){try {for (int i = 1; i <= fileCount; i++) {String deleteFilePath = Constant.PATH + Constant.FILE_NAME_START + Constant.SEPARATION + fileName + "#" + i + Constant.SEPARATION + Constant.FILE_NAME_END + Constant.TXT_SUFFIX;// 获取源文件和目标文件的输入输出流File file = new File(deleteFilePath);file.delete();Thread.sleep(2000);}} catch (InterruptedException e) {e.printStackTrace();}}
如果需要全部代码可以进行下载:
https://download.csdn.net/download/simon_09010817/11716181
这篇关于MybatisGenerator以及Mybatis配置和抽取反射进行分页查询并批量写入磁盘文件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!