Mybitis根据工具类反射数据库生成映射+整合springboot
一 反向生成数据库mapper的工具类:
添加依赖
<dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <scope>test</scope> <version>1.3.2</version> <optional>true</optional> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.1</version> </dependency>
工具类
package org.mybatis.generator.plugins; import org.mybatis.generator.api.IntrospectedTable; import org.mybatis.generator.api.PluginAdapter; import org.mybatis.generator.api.dom.java.*; import org.mybatis.generator.api.dom.xml.Attribute; import org.mybatis.generator.api.dom.xml.TextElement; import org.mybatis.generator.api.dom.xml.XmlElement; import java.util.List; public class AddLimitOffsetPlugin extends PluginAdapter { public boolean validate(List<String> warnings) { return true; } public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) { FullyQualifiedJavaType offsetType = new FullyQualifiedJavaType( "javax.annotation.Generated"); topLevelClass.addImportedType(offsetType); topLevelClass.addAnnotation("@Generated(\"" + introspectedTable.getTableConfiguration().getTableName() + "\")"); PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper(); Field limit = new Field(); limit.setName("limit"); limit.setVisibility(JavaVisibility.PRIVATE); limit.setType(integerWrapper); topLevelClass.addField(limit); Method limitSet = new Method(); limitSet.setVisibility(JavaVisibility.PUBLIC); limitSet.setName("setLimit"); limitSet.addParameter(new Parameter(integerWrapper, "limit")); limitSet.addBodyLine("this.limit = limit;"); topLevelClass.addMethod(limitSet); Method limitGet = new Method(); limitGet.setVisibility(JavaVisibility.PUBLIC); limitGet.setReturnType(integerWrapper); limitGet.setName("getLimit"); limitGet.addBodyLine("return limit;"); topLevelClass.addMethod(limitGet); Field offset = new Field(); offset.setName("offset"); offset.setVisibility(JavaVisibility.PRIVATE); offset.setType(integerWrapper); topLevelClass.addField(offset); Method offsetSet = new Method(); offsetSet.setVisibility(JavaVisibility.PUBLIC); offsetSet.setName("setOffset"); offsetSet.addParameter(new Parameter(integerWrapper, "offset")); offsetSet.addBodyLine("this.offset = offset;"); topLevelClass.addMethod(offsetSet); Method offsetGet = new Method(); offsetGet.setVisibility(JavaVisibility.PUBLIC); offsetGet.setReturnType(integerWrapper); offsetGet.setName("getOffset"); offsetGet.addBodyLine("return offset;"); topLevelClass.addMethod(offsetGet); return true; } public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated( XmlElement element, IntrospectedTable introspectedTable) { XmlElement isNotNullElement = new XmlElement("if"); isNotNullElement.addAttribute(new Attribute("test", "limit > 0")); isNotNullElement.addElement(new TextElement("limit ${limit}")); element.getElements().add(isNotNullElement); isNotNullElement = new XmlElement("if"); isNotNullElement.addAttribute(new Attribute("test", "offset > 0")); isNotNullElement.addElement(new TextElement("offset ${offset}")); element.getElements().add(isNotNullElement); return true; } }
package org.mybatis.generator.plugins; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.api.ProgressCallback; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.Context; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.internal.DefaultShellCallback; import java.io.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * mybatis代码生成插件调用者. */ public class Generator { @Test public void generate() throws Exception { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; InputStream stream = Generator.class.getClassLoader().getResourceAsStream("generatorConfig.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); InputStreamReader reader = new InputStreamReader(stream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(reader); String line = bufferedReader.readLine(); StringBuffer buffer = new StringBuffer(); while (line != null) { buffer.append(line + "\n"); line = bufferedReader.readLine(); } String xmlWithParam = buffer.toString(); System.out.println("------- xml config begin -------"); System.out.println(xmlWithParam); System.out.println("------- xml config end -------"); final Configuration config = cp.parseConfiguration(new ByteArrayInputStream(xmlWithParam.getBytes("utf-8"))); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); System.out.println("------- generator begin -------"); ProgressCallback cb = new ProgressCallback() { private Pattern SqlProviderPattern = Pattern.compile("\\w*SqlProvider\\.java"); private List<String> taskNames = new ArrayList(); @Override public void startTask(String taskName) { /* * 输出这些信息从而知道 生成那些类了 * Generating Example class for table demo * Generating Primary Key class for table demo * Generating Record class for table demo * Generating Mapper Interface for table demo * Generating SQL Provider for table demo * Saving file DemoExample.java * Saving file DemoKey.java Saving file Demo.java * Saving file DemoMapper.java * Saving file DemoSqlProvider.java */ // System.out.println(taskName); taskNames.add( taskName); } @Override public void saveStarted(int arg0) {} @Override public void introspectionStarted(int arg0) {} @Override public void generationStarted(int arg0) {} @Override public void done() { for(String taskName : taskNames){ Matcher matcher = SqlProviderPattern.matcher(taskName); if (matcher.find()) { final String SqlProviderFilename = matcher.group(); System.out.println("处理生成文件,selectByExample 增加mysql分页: " + SqlProviderFilename); List<Context> contexts = config.getContexts(); FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return SqlProviderFilename.equalsIgnoreCase(name); } }; boolean done = false; for (Context ctx : contexts) { if(done){ break; } String targetProject = ctx.getJavaClientGeneratorConfiguration().getTargetProject(); String targetPackage = ctx.getJavaClientGeneratorConfiguration().getTargetPackage(); String dir = targetProject.replaceAll("\\.", "\\\\") + "\\" + targetPackage.replaceAll("\\.", "\\\\"); System.out.println(System.getProperty("user.dir") + dir); File mapperDir = new File(System.getProperty("user.dir"), dir); File[] files = mapperDir.listFiles(filter); if (files != null && files.length > 0) { File sqlProviderFile = files[0]; try { Generator.addMysqlLimit(sqlProviderFile); done = true; } catch (Exception e) { e.printStackTrace(); } } } if(!done){ System.out.println("转换失败!!!! selectByExample 增加mysql分页: " + SqlProviderFilename); } else { System.out.println("转换成功!!!! selectByExample 增加mysql分页: " + SqlProviderFilename); } } } } @Override public void checkCancel() throws InterruptedException {} }; myBatisGenerator.generate(cb); for (String warning : warnings) { System.out.println(warning); } System.out.println("------- generator end -------"); // System.out.println(config.getClassPathEntries()); } private static void addMysqlLimit(File sqlProviderFile) throws Exception { /* * 这是自动生成的selectByExample 中的代码片段 if (example != null && example.getOrderByClause() != null) { ORDER_BY(example.getOrderByClause()); // 整个文件唯一 } return SQL(); * 将return SQL()改为下面片段即可: String sqlString = SQL(); if (example != null && example.getLimit() != null) { sqlString += " limit " + example.getLimit(); } if (example != null && example.getOffset() != null) { sqlString += " offset " + example.getOffset(); } return sqlString; */ BufferedReader reader = new BufferedReader( new FileReader(sqlProviderFile)); List<String> lines = IOUtils.readLines(reader); reader.close(); String limitString = " String sqlString = SQL();\n" + " if (example != null && example.getLimit() != null) {\n" + " sqlString += \" limit \" + example.getLimit();\n" + " }\n" + " if (example != null && example.getOffset() != null) {\n" + " sqlString += \" offset \" + example.getOffset();\n" + " }\n" + " return sqlString;"; ArrayList<String> newLines = new ArrayList<String>(); for (int i=0; i< lines.size();++i) { String line = lines.get(i); newLines.add(line ); if(line.replaceAll(" ", "") .equalsIgnoreCase("ORDER_BY(example.getOrderByClause());")) { // 添加下一行大括号和空白行 ++i; newLines.add(lines.get(i)); ++i; newLines.add(lines.get(i)); ++i; // 跳过 return SQL(); newLines.addAll(Arrays.asList( limitString.split("\n"))); } } // for (String line : newLines) { // System.out.println(line); // } FileOutputStream writer = new FileOutputStream(sqlProviderFile); IOUtils.writeLines(newLines, "\n",writer,"UTF-8"); writer.close(); } public static void main(String[] args) throws Exception { new Generator().generate(); } }
映射关系配置xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration> <context id="entmobile" targetRuntime="MyBatis3"> <plugin type="org.mybatis.generator.plugins.AddLimitOffsetPlugin" /> <plugin type="org.mybatis.generator.plugins.ToStringPlugin" /> <commentGenerator> <property name="suppressDate" value="true" /> <!-- remove comments --> <property name="suppressAllComments" value="true" /> </commentGenerator> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC" userId="root" password="root" /> <!-- java type resolver --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- gem entity --> <!-- data --> <javaModelGenerator targetPackage="com.自己建的实体类包.domain" targetProject="./src/main/java"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="false" /> </javaModelGenerator> <!-- //////////////////////////////////////////////////////////////////////////////////////////////////////////////// --> <!-- gem annotated mapper --> <!-- data --> <javaClientGenerator type="ANNOTATEDMAPPER" targetPackage="com.自己建的mapper包.mapper" targetProject="./src/main/java"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- <table tableName="material_type"> <generatedKey column="id" sqlStatement="JDBC"/> </table> --> <table tableName="表名"> <generatedKey column="id" sqlStatement="JDBC"/> </table> </context> </generatorConfiguration>
另外springboot启动类中添加注解:
@EnableAutoConfiguration
二 数据库配置 application.yml:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&connectTimeout=3000&socketTimeout=1000 username: root # 自己修改填写 password: root # 自己修改填写 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver minIdle: 5 maxActive: 100 initialSize: 10 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: select 'x' testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 50 removeAbandoned: true filters: stat # ,wall,log4j # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据 druidLoginName: wjf # 登录druid的账号 druidPassword: wjf # 登录druid的密码 cachePrepStmts: true # 开启二级缓存 redis: database: 0 host: XX.XX.XX.XX port: 6380 password: 1234567 activemq: broker-url: tcp://XX.XX.XX.XX:61616 user: admin password: zhangjiawen pool: enabled: true max-connections: 100 mybatis: typeAliasesPackage: com.concurrent.mapper # 自己修改填写 mapperLocations: classpath:/com/concurrent/mapper/*.xml mapperScanPackage: com.concurrent.mapper configLocation: classpath:/mybatis-config.xml
mybatis-config.xml:
<?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> <settings> <!-- 使全局的映射器启用或禁用缓存。 --> <setting name="cacheEnabled" value="true" /> <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 --> <setting name="aggressiveLazyLoading" value="true"/> <!-- 是否允许单条sql 返回多个数据集 (取决于驱动的兼容性) default:true --> <setting name="multipleResultSetsEnabled" value="true" /> <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true --> <setting name="useColumnLabel" value="true" /> <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false --> <setting name="useGeneratedKeys" value="false" /> <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分 FULL:全部 --> <setting name="autoMappingBehavior" value="PARTIAL" /> <!-- 这是默认的执行类型 (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新) --> <setting name="defaultExecutorType" value="SIMPLE" /> <setting name="defaultStatementTimeout" value="25" /> <setting name="defaultFetchSize" value="100" /> <setting name="safeRowBoundsEnabled" value="false" /> <!-- 使用驼峰命名法转换字段。 --> <setting name="mapUnderscoreToCamelCase" value="true" /> <!-- 设置本地缓存范围 session:就会有数据的共享 statement:语句范围 (这样就不会有数据的共享 ) defalut:session --> <setting name="localCacheScope" value="SESSION" /> <!-- 默认为OTHER,为了解决oracle插入null报错的问题要设置为NULL --> <setting name="jdbcTypeForNull" value="NULL" /> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" /> </settings> </configuration>
另外还有将mapper数据映射加入spring扫描
package com.concurrent.service; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import javax.sql.DataSource; import java.sql.SQLException; /** * Created on 2016/10/25 0025. */ @Configuration @MapperScan("com.自己建的映射包.mapper") @EnableRedisHttpSession(maxInactiveIntervalInSeconds= 50) public class DataSourceConfig { private Logger logger = LoggerFactory.getLogger(DataSourceConfig.class); @Value("${spring.datasource.url}") private String dbUrl; @Value("${spring.datasource.type}") private String dbType; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Value("${spring.datasource.driver-class-name}") private String driverClassName; @Value("${spring.datasource.initialSize}") private int initialSize; @Value("${spring.datasource.minIdle}") private int minIdle; @Value("${spring.datasource.maxActive}") private int maxActive; @Value("${spring.datasource.maxWait}") private int maxWait; @Value("${spring.datasource.timeBetweenEvictionRunsMillis}") private int timeBetweenEvictionRunsMillis; @Value("${spring.datasource.minEvictableIdleTimeMillis}") private int minEvictableIdleTimeMillis; @Value("${spring.datasource.validationQuery}") private String validationQuery; @Value("${spring.datasource.testWhileIdle}") private boolean testWhileIdle; @Value("${spring.datasource.testOnBorrow}") private boolean testOnBorrow; @Value("${spring.datasource.testOnReturn}") private boolean testOnReturn; @Value("${spring.datasource.poolPreparedStatements}") private boolean poolPreparedStatements; @Value("${spring.datasource.filters}") private String filters; @Value("${spring.datasource.connectionProperties}") private String connectionProperties; @Value("${spring.datasource.useGlobalDataSourceStat}") private boolean useGlobalDataSourceStat; @Value("${spring.datasource.druidLoginName}") private String druidLoginName; @Value("${spring.datasource.druidPassword}") private String druidPassword; @Bean(name="dataSource",destroyMethod = "close", initMethod="init") @Primary //不要漏了这 public DataSource dataSource(){ DruidDataSource datasource = new DruidDataSource(); try { datasource.setUrl(this.dbUrl); datasource.setDbType(dbType); datasource.setUsername(username); datasource.setPassword(password); datasource.setDriverClassName(driverClassName); datasource.setInitialSize(initialSize); datasource.setMinIdle(minIdle); datasource.setMaxActive(maxActive); datasource.setMaxWait(maxWait); datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); datasource.setValidationQuery(validationQuery); datasource.setTestWhileIdle(testWhileIdle); datasource.setTestOnBorrow(testOnBorrow); datasource.setTestOnReturn(testOnReturn); datasource.setPoolPreparedStatements(poolPreparedStatements); datasource.setFilters(filters); } catch (SQLException e) { logger.error("druid configuration initialization filter", e); } return datasource; } ///////// 下面是druid 监控访问的设置 ///////////////// @Bean public ServletRegistrationBean druidServlet() { ServletRegistrationBean reg = new ServletRegistrationBean(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); //url 匹配 reg.addInitParameter("allow", "192.168.16.110,127.0.0.1"); // IP白名单 (没有配置或者为空,则允许所有访问) reg.addInitParameter("deny", "192.168.16.111"); //IP黑名单 (存在共同时,deny优先于allow) reg.addInitParameter("loginUsername", this.druidLoginName);//登录名 reg.addInitParameter("loginPassword", this.druidPassword);//登录密码 reg.addInitParameter("resetEnable", "false"); // 禁用HTML页面上的“Reset All”功能 return reg; } @Bean(name="druidWebStatFilter") public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); //忽略资源 filterRegistrationBean.addInitParameter("profileEnable", "true"); filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE"); filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION"); return filterRegistrationBean; } }
使用例子:
@Resource private SysUserMapper sysUserMapper; private BloomFilter<Integer> bf; /*** * PostConstruct 程序启动时候加载此方法 */ @PostConstruct public void initBloomFilter() { SysUserExample sysUserExample = new SysUserExample(); List<SysUser> sysUserList = sysUserMapper.selectByExample(sysUserExample); }