jobob пре 8 година
родитељ
комит
a5fc9b320a
16 измењених фајлова са 2271 додато и 15 уклоњено
  1. 4 15
      mybatis-plus/pom.xml
  2. 182 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/AbstractGenerator.java
  3. 274 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/MybatisPlusGenerator.java
  4. 74 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/ConstVal.java
  5. 123 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/DataSourceConfig.java
  6. 136 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/PackageConfig.java
  7. 169 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/StrategyConfig.java
  8. 88 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/TemplateConfig.java
  9. 569 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/builder/ConfigBuilder.java
  10. 85 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/po/TableField.java
  11. 166 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/po/TableInfo.java
  12. 37 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/DbType.java
  13. 37 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/IdStrategy.java
  14. 139 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/NamingStrategy.java
  15. 111 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/QuerySQL.java
  16. 77 0
      mybatis-plus/src/main/java/com/baomidou/mybatisplus/toolkit/StringUtils.java

+ 4 - 15
mybatis-plus/pom.xml

@@ -48,6 +48,7 @@
 		<mybatis-ehcache.version>1.0.3</mybatis-ehcache.version>
 		<junit.version>4.12</junit.version>
 		<contiperf.version>2.3.4</contiperf.version>
+		<velocity.version>1.7</velocity.version>
 	</properties>
 
 	<dependencies>
@@ -143,21 +144,9 @@
 			<scope>test</scope>
 		</dependency>
 		<dependency>
-			<groupId>org.xerial</groupId>
-			<artifactId>sqlite-jdbc</artifactId>
-			<version>3.14.2.1</version>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>postgresql</groupId>
-			<artifactId>postgresql</artifactId>
-			<version>9.1-901-1.jdbc4</version>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.hsqldb</groupId>
-			<artifactId>hsqldb</artifactId>
-			<version>2.3.4</version>
+			<groupId>org.apache.velocity</groupId>
+			<artifactId>velocity</artifactId>
+			<version>${velocity.version}</version>
 			<scope>test</scope>
 		</dependency>
 		<!-- test end -->

+ 182 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/AbstractGenerator.java

@@ -0,0 +1,182 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator;
+
+import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
+import com.baomidou.mybatisplus.generator.config.PackageConfig;
+import com.baomidou.mybatisplus.generator.config.StrategyConfig;
+import com.baomidou.mybatisplus.generator.config.TemplateConfig;
+import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
+
+/**
+ * 插件基类,用于属性配置 设计成抽象类主要是用于后期可扩展,共享参数配置。
+ *
+ * @author YangHu
+ * @since 2016/8/30
+ */
+public abstract class AbstractGenerator {
+
+	
+	/**
+	 * 数据源配置
+	 */
+	private DataSourceConfig dataSource;
+
+	/**
+	 * 数据库表配置
+	 */
+	private StrategyConfig strategy;
+
+	/**
+	 * 包 相关配置
+	 */
+	private PackageConfig packageInfo;
+
+	/**
+	 * 模板 相关配置
+	 */
+	private TemplateConfig template;
+
+	/**
+	 * 生成文件的输出目录
+	 */
+	private String outputDir;
+
+	/**
+	 * 是否覆盖已有文件
+	 */
+	private boolean fileOverride = false;
+
+	/**
+	 * 是否打开输出目录
+	 */
+	private boolean open = true;
+
+	/**
+	 * 是否在xml中添加二级缓存配置
+	 */
+	private boolean enableCache = true;
+
+	/**
+	 * 开发人员
+	 */
+	private String author;
+
+	/**
+	 * 开启 ActiveRecord 模式
+	 */
+	private boolean activeRecord = true;
+
+	protected ConfigBuilder config;
+
+	/**
+	 * 初始化配置
+	 */
+	protected void initConfig() {
+		if (null == config) {
+			config = new ConfigBuilder(packageInfo, dataSource, strategy, template, outputDir);
+		}
+	}
+
+	public DataSourceConfig getDataSource() {
+		return dataSource;
+	}
+
+	public void setDataSource(DataSourceConfig dataSource) {
+		this.dataSource = dataSource;
+	}
+
+	public StrategyConfig getStrategy() {
+		return strategy;
+	}
+
+	public void setStrategy(StrategyConfig strategy) {
+		this.strategy = strategy;
+	}
+
+	public PackageConfig getPackageInfo() {
+		return packageInfo;
+	}
+
+	public void setPackageInfo(PackageConfig packageInfo) {
+		this.packageInfo = packageInfo;
+	}
+
+	public TemplateConfig getTemplate() {
+		return template;
+	}
+
+	public void setTemplate(TemplateConfig template) {
+		this.template = template;
+	}
+
+	public String getOutputDir() {
+		return outputDir;
+	}
+
+	public void setOutputDir(String outputDir) {
+		this.outputDir = outputDir;
+	}
+
+	public boolean isFileOverride() {
+		return fileOverride;
+	}
+
+	public void setFileOverride(boolean fileOverride) {
+		this.fileOverride = fileOverride;
+	}
+
+	public boolean isOpen() {
+		return open;
+	}
+
+	public void setOpen(boolean open) {
+		this.open = open;
+	}
+
+	public boolean isEnableCache() {
+		return enableCache;
+	}
+
+	public void setEnableCache(boolean enableCache) {
+		this.enableCache = enableCache;
+	}
+
+	public String getAuthor() {
+		return author;
+	}
+
+	public void setAuthor(String author) {
+		this.author = author;
+	}
+
+	public boolean isActiveRecord() {
+		return activeRecord;
+	}
+
+	public void setActiveRecord(boolean activeRecord) {
+		this.activeRecord = activeRecord;
+	}
+
+	public ConfigBuilder getConfig() {
+		return config;
+	}
+
+	public void setConfig(ConfigBuilder config) {
+		this.config = config;
+	}
+
+}

+ 274 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/MybatisPlusGenerator.java

@@ -0,0 +1,274 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+
+import com.baomidou.mybatisplus.generator.config.ConstVal;
+import com.baomidou.mybatisplus.generator.config.TemplateConfig;
+import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+
+/**
+ * 生成文件
+ * 
+ * @author YangHu, tangguo
+ * @since 2016/8/30
+ */
+public class MybatisPlusGenerator extends AbstractGenerator {
+
+	private static final Log logger = LogFactory.getLog(MybatisPlusGenerator.class);
+
+	/**
+	 * velocity引擎
+	 */
+	private VelocityEngine engine;
+
+	/**
+	 * 输出文件
+	 */
+	private Map<String, String> outputFiles;
+
+	public void execute() {
+		logger.debug("==========================准备生成文件...==========================");
+		// 初始化配置
+		initConfig();
+		// 初始化输出文件路径模板
+		initOutputFiles();
+		// 创建输出文件路径
+		mkdirs(config.getPathInfo());
+		// 获取上下文
+		Map<String, VelocityContext> ctxData = analyzeData(config);
+		// 循环生成文件
+		for (Map.Entry<String, VelocityContext> ctx : ctxData.entrySet()) {
+			batchOutput(ctx.getKey(), ctx.getValue());
+		}
+		// 打开输出目录
+		if (isOpen()) {
+			try {
+				String osName = System.getProperty("os.name");
+				if (osName != null) {
+					if (osName.contains("Mac")) {
+						Runtime.getRuntime().exec("open " + getOutputDir());
+					} else if (osName.contains("Windows")) {
+						Runtime.getRuntime().exec("cmd /c start " + getOutputDir());
+					} else {
+						logger.debug("文件输出目录:" + getOutputDir());
+					}
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		logger.debug("==========================文件生成完成!!!==========================");
+	}
+
+	/**
+	 * 分析数据
+	 *
+	 * @param config
+	 *            总配置信息
+	 * @return 解析数据结果集
+	 */
+	private Map<String, VelocityContext> analyzeData(ConfigBuilder config) {
+		List<TableInfo> tableList = config.getTableInfoList();
+		Map<String, String> packageInfo = config.getPackageInfo();
+		Map<String, VelocityContext> ctxData = new HashMap<String, VelocityContext>();
+		String superEntityClass = getSuperClassName(config.getSuperEntityClass());
+		String superMapperClass = getSuperClassName(config.getSuperMapperClass());
+		String superServiceClass = getSuperClassName(config.getSuperServiceClass());
+		String superServiceImplClass = getSuperClassName(config.getSuperServiceImplClass());
+		String superControllerClass = getSuperClassName(config.getSuperControllerClass());
+		String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+
+		for (TableInfo tableInfo : tableList) {
+			VelocityContext ctx = new VelocityContext();
+			ctx.put("package", packageInfo);
+			ctx.put("table", tableInfo);
+			ctx.put("entity", tableInfo.getEntityName());
+			ctx.put("addTabeName", !tableInfo.getEntityName().toLowerCase().equals(tableInfo.getName().toLowerCase()));
+			ctx.put("idGenType", config.getIdType());
+			ctx.put("superEntityClassPackage", config.getSuperEntityClass());
+			ctx.put("superEntityClass", superEntityClass);
+			ctx.put("superMapperClassPackage", config.getSuperMapperClass());
+			ctx.put("superMapperClass", superMapperClass);
+			ctx.put("superServiceClassPackage", config.getSuperServiceClass());
+			ctx.put("superServiceClass", superServiceClass);
+			ctx.put("superServiceImplClassPackage", config.getSuperServiceImplClass());
+			ctx.put("superServiceImplClass", superServiceImplClass);
+			ctx.put("superControllerClassPackage", config.getSuperControllerClass());
+			ctx.put("superControllerClass", superControllerClass);
+			ctx.put("enableCache", isEnableCache());
+			ctx.put("author", getAuthor());
+			ctx.put("activeRecord", isActiveRecord());
+			ctx.put("date", date);
+			ctxData.put(tableInfo.getEntityName(), ctx);
+		}
+		return ctxData;
+	}
+
+	/**
+	 * 获取类名
+	 * 
+	 * @param classPath
+	 * @return
+	 */
+	private String getSuperClassName(String classPath) {
+		if (StringUtils.isBlank(classPath))
+			return null;
+		return classPath.substring(classPath.lastIndexOf(".") + 1);
+	}
+
+	/**
+	 * 处理输出目录
+	 *
+	 * @param pathInfo
+	 *            路径信息
+	 */
+	private void mkdirs(Map<String, String> pathInfo) {
+		for (Map.Entry<String, String> entry : pathInfo.entrySet()) {
+			File dir = new File(entry.getValue());
+			if (!dir.exists()) {
+				boolean result = dir.mkdirs();
+				if (result) {
+					logger.debug("创建目录: [" + entry.getValue() + "]");
+				}
+			}
+		}
+	}
+
+	/**
+	 * 初始化输出目录
+	 */
+	private void initOutputFiles() {
+		outputFiles = new HashMap<String, String>();
+		Map<String, String> pathInfo = config.getPathInfo();
+		outputFiles.put(ConstVal.ENTITY, pathInfo.get(ConstVal.ENTITY_PATH) + ConstVal.ENTITY_NAME);
+		outputFiles.put(ConstVal.MAPPER, pathInfo.get(ConstVal.MAPPER_PATH) + ConstVal.MAPPER_NAME);
+		outputFiles.put(ConstVal.XML, pathInfo.get(ConstVal.XML_PATH) + ConstVal.XML_NAME);
+		outputFiles.put(ConstVal.SERIVCE, pathInfo.get(ConstVal.SERIVCE_PATH) + ConstVal.SERVICE_NAME);
+		outputFiles.put(ConstVal.SERVICEIMPL, pathInfo.get(ConstVal.SERVICEIMPL_PATH) + ConstVal.SERVICEIMPL_NAME);
+		outputFiles.put(ConstVal.CONTROLLER, pathInfo.get(ConstVal.CONTROLLER_PATH) + ConstVal.CONTROLLER_NAME);
+	}
+
+	/**
+	 * 合成上下文与模板
+	 *
+	 * @param context
+	 *            vm上下文
+	 */
+	private void batchOutput(String entityName, VelocityContext context) {
+		try {
+			String entityFile = String.format(outputFiles.get(ConstVal.ENTITY), entityName);
+			String mapperFile = String.format(outputFiles.get(ConstVal.MAPPER), entityName);
+			String xmlFile = String.format(outputFiles.get(ConstVal.XML), entityName);
+			String serviceFile = String.format(outputFiles.get(ConstVal.SERIVCE), entityName);
+			String implFile = String.format(outputFiles.get(ConstVal.SERVICEIMPL), entityName);
+			String controllerFile = String.format(outputFiles.get(ConstVal.CONTROLLER), entityName);
+
+			TemplateConfig template = config.getTemplate();
+
+			// 根据override标识来判断是否需要创建文件
+			if (isCreate(entityFile)) {
+				vmToFile(context, template.getEntity(), entityFile);
+			}
+			if (isCreate(mapperFile)) {
+				vmToFile(context, template.getMapper(), mapperFile);
+			}
+			if (isCreate(xmlFile)) {
+				vmToFile(context, template.getXml(), xmlFile);
+			}
+			if (isCreate(serviceFile)) {
+				vmToFile(context, template.getService(), serviceFile);
+			}
+			if (isCreate(implFile)) {
+				vmToFile(context, template.getServiceImpl(), implFile);
+			}
+			if (isCreate(controllerFile)) {
+				vmToFile(context, template.getController(), controllerFile);
+			}
+		} catch (IOException e) {
+			logger.error("无法创建文件,请检查配置信息!");
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 将模板转化成为文件
+	 *
+	 * @param context
+	 *            内容对象
+	 * @param templatePath
+	 *            模板文件
+	 * @param outputFile
+	 *            文件生成的目录
+	 */
+	private void vmToFile(VelocityContext context, String templatePath, String outputFile) throws IOException {
+		VelocityEngine velocity = getVelocityEngine();
+		Template template = velocity.getTemplate(templatePath, ConstVal.UTF8);
+		FileOutputStream fos = new FileOutputStream(outputFile);
+		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, ConstVal.UTF8));
+		template.merge(context, writer);
+		writer.close();
+		logger.debug("模板:" + templatePath + ";  文件:" + outputFile);
+	}
+
+	/**
+	 * 设置模版引擎,主要指向获取模版路径
+	 */
+	private VelocityEngine getVelocityEngine() {
+		if (engine == null) {
+			Properties p = new Properties();
+			p.setProperty(ConstVal.VM_LOADPATH_KEY, ConstVal.VM_LOADPATH_VALUE);
+			p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
+			p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8);
+			p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8);
+			p.setProperty(Velocity.OUTPUT_ENCODING, ConstVal.UTF8);
+			p.setProperty("file.resource.loader.unicode", "true");
+			engine = new VelocityEngine(p);
+		}
+		return engine;
+	}
+
+	/**
+	 * 检测文件是否存在
+	 *
+	 * @return 是否
+	 */
+	private boolean isCreate(String filePath) {
+		File file = new File(filePath);
+		return !file.exists() || isFileOverride();
+	}
+
+}

+ 74 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/ConstVal.java

@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+/**
+ * 定义常量
+ *
+ * @author YangHu, tangguo
+ * @since 2016/8/31
+ */
+public class ConstVal {
+	
+	public static final String MODULENAME = "ModuleName";
+	
+	public static final String ENTITY = "Entity";
+	public static final String SERIVCE = "Service";
+	public static final String SERVICEIMPL = "ServiceImpl";
+	public static final String MAPPER = "Mapper";
+	public static final String XML = "Xml";
+	public static final String CONTROLLER = "Controller";
+
+	public static final String ENTITY_PATH = "entity_path";
+	public static final String SERIVCE_PATH = "serivce_path";
+	public static final String SERVICEIMPL_PATH = "serviceimpl_path";
+	public static final String MAPPER_PATH = "mapper_path";
+	public static final String XML_PATH = "xml_path";
+	public static final String CONTROLLER_PATH = "controller_path";
+
+	public static final String JAVA_TMPDIR = "java.io.tmpdir";
+	public static final String UTF8 = Charset.forName("UTF-8").name();
+	public static final String UNDERLINE = "_";
+
+	public static final String JAVA_SUFFIX = ".java";
+	public static final String XML_SUFFIX = ".xml";
+
+	public static final String TEMPLATE_ENTITY = "/template/entity.java.vm";
+	public static final String TEMPLATE_MAPPER = "/template/mapper.java.vm";
+	public static final String TEMPLATE_XML = "/template/mapper.xml.vm";
+	public static final String TEMPLATE_SERVICE = "/template/service.java.vm";
+	public static final String TEMPLATE_SERVICEIMPL = "/template/serviceImpl.java.vm";
+	public static final String TEMPLATE_CONTROLLER = "/template/controller.java.vm";
+
+	public static final String ENTITY_NAME = File.separator + "%s" + JAVA_SUFFIX;
+	public static final String MAPPER_NAME = File.separator + "%s" + MAPPER + JAVA_SUFFIX;
+	public static final String XML_NAME = File.separator + "%s" + MAPPER + XML_SUFFIX;
+	public static final String SERVICE_NAME = File.separator + "I%s" + SERIVCE + JAVA_SUFFIX;
+	public static final String SERVICEIMPL_NAME = File.separator + "%s" + SERVICEIMPL + JAVA_SUFFIX;
+	public static final String CONTROLLER_NAME = File.separator + "%s" + CONTROLLER + JAVA_SUFFIX;
+
+	// 配置使用classloader加载资源
+	public static final String VM_LOADPATH_KEY = "file.resource.loader.class";
+	public static final String VM_LOADPATH_VALUE = "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader";
+
+	public static final String SUPERD_MAPPER_CLASS = "com.baomidou.mybatisplus.mapper.BaseMapper";
+	public static final String SUPERD_SERVICE_CLASS = "com.baomidou.framework.service.IService";
+	public static final String SUPERD_SERVICEIMPL_CLASS = "com.baomidou.framework.service.impl.ServiceImpl";
+	
+}

+ 123 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/DataSourceConfig.java

@@ -0,0 +1,123 @@
+package com.baomidou.mybatisplus.generator.config;
+
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+import com.baomidou.mybatisplus.generator.config.rules.DbType;
+
+/**
+ * 数据库配置
+ *
+ * @author YangHu
+ * @since 2016/8/30
+ */
+public class DataSourceConfig {
+
+	/**
+	 * 数据库类型
+	 */
+	private DbType dbType;
+	/**
+	 * 驱动连接的URL
+	 */
+	private String url;
+	/**
+	 * 驱动名称
+	 */
+	private String driverName;
+	/**
+	 * 数据库连接用户名
+	 */
+	private String username;
+	/**
+	 * 数据库连接密码
+	 */
+	private String password;
+
+	/**
+	 * 判断数据库类型
+	 *
+	 * @return 类型枚举值
+	 */
+	public DbType getDbType() {
+		if (null == dbType) {
+			if (driverName.contains("mysql")) {
+				dbType = DbType.MYSQL;
+			} else if (driverName.contains("oracle")) {
+				dbType = DbType.ORACLE;
+			}
+		}
+		return dbType;
+	}
+
+	/**
+	 * 创建数据库连接对象
+	 *
+	 * @return Connection
+	 */
+	public Connection getConn() {
+		Connection conn = null;
+		try {
+			Class.forName(driverName);
+			conn = DriverManager.getConnection(url, username, password);
+		} catch (ClassNotFoundException e) {
+			e.printStackTrace();
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+		return conn;
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	public String getDriverName() {
+		return driverName;
+	}
+
+	public void setDriverName(String driverName) {
+		this.driverName = driverName;
+	}
+
+	public String getUsername() {
+		return username;
+	}
+
+	public void setUsername(String username) {
+		this.username = username;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public void setDbType(DbType dbType) {
+		this.dbType = dbType;
+	}
+
+}

+ 136 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/PackageConfig.java

@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config;
+
+import com.baomidou.mybatisplus.toolkit.StringUtils;
+
+/**
+ * 跟包相关的配置项
+ *
+ * @author YangHu, tangguo
+ * @since 2016/8/30
+ */
+public class PackageConfig {
+
+	/**
+	 * 父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名
+	 */
+	private String parent;
+
+	/**
+	 * 父包模块名。
+	 */
+	private String moduleName;
+
+	/**
+	 * Entity包名
+	 */
+	private String entity;
+
+	/**
+	 * Service包名
+	 */
+	private String service;
+
+	/**
+	 * Service Impl包名
+	 */
+	private String serviceImpl;
+	/**
+	 * Mapper包名
+	 */
+	private String mapper;
+
+	/**
+	 * Mapper XML包名
+	 */
+	private String xml;
+
+	/**
+	 * Controller包名
+	 */
+	private String controller;
+
+	public String getParent() {
+		if (moduleName != null && !"".equals(moduleName.trim()))
+			return parent + "." + moduleName;
+		return parent;
+	}
+
+	public String getModuleName() {
+		return moduleName;
+	}
+
+	public void setModuleName(String moduleName) {
+		this.moduleName = moduleName;
+	}
+
+	public String getEntity() {
+		return entity;
+	}
+
+	public void setEntity(String entity) {
+		this.entity = entity;
+	}
+
+	public String getService() {
+		return service;
+	}
+
+	public void setService(String service) {
+		this.service = service;
+	}
+
+	public String getServiceImpl() {
+		return serviceImpl;
+	}
+
+	public void setServiceImpl(String serviceImpl) {
+		this.serviceImpl = serviceImpl;
+	}
+
+	public String getMapper() {
+		return mapper;
+	}
+
+	public void setMapper(String mapper) {
+		this.mapper = mapper;
+	}
+
+	public String getXml() {
+		return xml;
+	}
+
+	public void setXml(String xml) {
+		this.xml = xml;
+	}
+
+	public void setParent(String parent) {
+		this.parent = parent;
+	}
+
+	public String getController() {
+		if (StringUtils.isEmpty(controller)) {
+			return "web";
+		}
+		return controller;
+	}
+
+	public void setController(String controller) {
+		this.controller = controller;
+	}
+
+}

+ 169 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/StrategyConfig.java

@@ -0,0 +1,169 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config;
+
+import com.baomidou.mybatisplus.generator.config.rules.IdStrategy;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+
+/**
+ * 策略配置项
+ *
+ * @author YangHu, tangguo, hubin
+ * @since 2016/8/30
+ */
+public class StrategyConfig {
+
+	/**
+	 * 数据库表映射到实体的命名策略
+	 */
+	private NamingStrategy naming;
+
+	private NamingStrategy fieldNaming;
+
+	/**
+	 * 表前缀
+	 */
+	private String tablePrefix;
+
+	/**
+	 * Entity 中的ID生成类型
+	 */
+	private IdStrategy idGenType;
+
+	/**
+	 * 自定义继承的Entity类全称,带包名
+	 */
+	private String superEntityClass;
+
+	/**
+	 * 自定义继承的Mapper类全称,带包名
+	 */
+	private String superMapperClass;
+
+	/**
+	 * 自定义继承的Service类全称,带包名
+	 */
+	private String superServiceClass;
+
+	/**
+	 * 自定义继承的ServiceImpl类全称,带包名
+	 */
+	private String superServiceImplClass;
+
+	/**
+	 * 自定义继承的Controller类全称,带包名
+	 */
+	private String superControllerClass;
+
+	/*
+	 * 需要包含的表名(与exclude二选一配置)
+	 */
+	private String[] include = null;
+
+	/**
+	 * 需要排除的表名
+	 */
+	private String[] exclude = null;
+
+	public NamingStrategy getNaming() {
+		return naming;
+	}
+
+	public void setNaming(NamingStrategy naming) {
+		this.naming = naming;
+	}
+
+	public NamingStrategy getFieldNaming() {
+		return fieldNaming;
+	}
+
+	public void setFieldNaming(NamingStrategy fieldNaming) {
+		this.fieldNaming = fieldNaming;
+	}
+
+	public String getTablePrefix() {
+		return tablePrefix;
+	}
+
+	public void setTablePrefix(String tablePrefix) {
+		this.tablePrefix = tablePrefix;
+	}
+
+	public IdStrategy getIdGenType() {
+		return idGenType;
+	}
+
+	public void setIdGenType(IdStrategy idGenType) {
+		this.idGenType = idGenType;
+	}
+
+	public String getSuperEntityClass() {
+		return superEntityClass;
+	}
+
+	public void setSuperEntityClass(String superEntityClass) {
+		this.superEntityClass = superEntityClass;
+	}
+
+	public String getSuperMapperClass() {
+		return superMapperClass;
+	}
+
+	public void setSuperMapperClass(String superMapperClass) {
+		this.superMapperClass = superMapperClass;
+	}
+
+	public String getSuperServiceClass() {
+		return superServiceClass;
+	}
+
+	public void setSuperServiceClass(String superServiceClass) {
+		this.superServiceClass = superServiceClass;
+	}
+
+	public String getSuperServiceImplClass() {
+		return superServiceImplClass;
+	}
+
+	public void setSuperServiceImplClass(String superServiceImplClass) {
+		this.superServiceImplClass = superServiceImplClass;
+	}
+
+	public String getSuperControllerClass() {
+		return superControllerClass;
+	}
+
+	public void setSuperControllerClass(String superControllerClass) {
+		this.superControllerClass = superControllerClass;
+	}
+
+	public String[] getInclude() {
+		return include;
+	}
+
+	public void setInclude(String[] include) {
+		this.include = include;
+	}
+
+	public String[] getExclude() {
+		return exclude;
+	}
+
+	public void setExclude(String[] exclude) {
+		this.exclude = exclude;
+	}
+
+}

+ 88 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/TemplateConfig.java

@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config;
+
+/**
+ * 
+ * @ClassName: TemplateConfig
+ * @Description: 模板路径配置项
+ * @author tzg
+ * @date 2016年11月10日 下午4:45:12
+ *
+ */
+public class TemplateConfig {
+
+	private String entity;
+
+	private String service;
+
+	private String serviceImpl;
+
+	private String mapper;
+
+	private String xml;
+
+	private String controller;
+
+	public String getEntity() {
+		return entity;
+	}
+
+	public void setEntity(String entity) {
+		this.entity = entity;
+	}
+
+	public String getService() {
+		return service;
+	}
+
+	public void setService(String service) {
+		this.service = service;
+	}
+
+	public String getServiceImpl() {
+		return serviceImpl;
+	}
+
+	public void setServiceImpl(String serviceImpl) {
+		this.serviceImpl = serviceImpl;
+	}
+
+	public String getMapper() {
+		return mapper;
+	}
+
+	public void setMapper(String mapper) {
+		this.mapper = mapper;
+	}
+
+	public String getXml() {
+		return xml;
+	}
+
+	public void setXml(String xml) {
+		this.xml = xml;
+	}
+
+	public String getController() {
+		return controller;
+	}
+
+	public void setController(String controller) {
+		this.controller = controller;
+	}
+
+}

+ 569 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/builder/ConfigBuilder.java

@@ -0,0 +1,569 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config.builder;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.baomidou.mybatisplus.generator.config.ConstVal;
+import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
+import com.baomidou.mybatisplus.generator.config.PackageConfig;
+import com.baomidou.mybatisplus.generator.config.StrategyConfig;
+import com.baomidou.mybatisplus.generator.config.TemplateConfig;
+import com.baomidou.mybatisplus.generator.config.po.TableField;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.DbType;
+import com.baomidou.mybatisplus.generator.config.rules.IdStrategy;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+import com.baomidou.mybatisplus.generator.config.rules.QuerySQL;
+import com.baomidou.mybatisplus.toolkit.StringUtils;
+
+/**
+ * 配置汇总 传递给文件生成工具
+ *
+ * @author YangHu, tangguo, hubin
+ * @since 2016/8/30
+ */
+public class ConfigBuilder {
+
+	/**
+	 * SQL连接
+	 */
+	private Connection connection;
+	/**
+	 * SQL语句类型
+	 */
+	private QuerySQL querySQL;
+	private String superEntityClass;
+	private String superMapperClass;
+	/**
+	 * service超类定义
+	 */
+	private String superServiceClass;
+	private String superServiceImplClass;
+	private String superControllerClass;
+	/**
+	 * ID的字符串类型
+	 */
+	private String idType;
+	/**
+	 * 数据库表信息
+	 */
+	private List<TableInfo> tableInfoList;
+
+	/**
+	 * 包配置详情
+	 */
+	private Map<String, String> packageInfo;
+	/**
+	 * 路径配置信息
+	 */
+	private Map<String, String> pathInfo;
+
+	/**
+	 * 模板路径配置信息
+	 */
+	private TemplateConfig template;
+
+	/**
+	 * 在构造器中处理配置
+	 *
+	 * @param outputDir
+	 *            输出目录
+	 * @param packageConfig
+	 *            包配置
+	 * @param dataSourceConfig
+	 *            数据源配置
+	 * @param strategyConfig
+	 *            表配置
+	 */
+	public ConfigBuilder(PackageConfig packageConfig, DataSourceConfig dataSourceConfig, StrategyConfig strategyConfig,
+			TemplateConfig template, String outputDir) {
+		handlerPackage(outputDir, packageConfig);
+		handlerDataSource(dataSourceConfig);
+		handlerStrategy(strategyConfig);
+		this.template = template;
+	}
+
+	// ************************ 曝露方法 BEGIN*****************************
+
+	/**
+	 * 所有包配置信息
+	 *
+	 * @return 包配置
+	 */
+	public Map<String, String> getPackageInfo() {
+		return packageInfo;
+	}
+
+	/**
+	 * 所有路径配置
+	 *
+	 * @return 路径配置
+	 */
+	public Map<String, String> getPathInfo() {
+		return pathInfo;
+	}
+
+	public String getSuperEntityClass() {
+		return superEntityClass;
+	}
+
+	public String getSuperMapperClass() {
+		return superMapperClass;
+	}
+
+	/**
+	 * 获取超类定义
+	 *
+	 * @return 完整超类名称
+	 */
+	public String getSuperServiceClass() {
+		return superServiceClass;
+	}
+
+	public String getSuperServiceImplClass() {
+		return superServiceImplClass;
+	}
+
+	public String getSuperControllerClass() {
+		return superControllerClass;
+	}
+
+	/**
+	 * 获取ID类型
+	 *
+	 * @return id生成方式
+	 */
+	public String getIdType() {
+		return idType;
+	}
+
+	/**
+	 * 表信息
+	 *
+	 * @return 所有表信息
+	 */
+	public List<TableInfo> getTableInfoList() {
+		return tableInfoList;
+	}
+
+	/**
+	 * 模板路径配置信息
+	 * 
+	 * @return 所以模板路径配置信息
+	 */
+	public TemplateConfig getTemplate() {
+		return template == null ? new TemplateConfig() : template;
+	}
+
+	// ****************************** 曝露方法 END**********************************
+
+	/**
+	 * 处理包配置
+	 *
+	 * @param config
+	 *            PackageConfig
+	 */
+	private void handlerPackage(String outputDir, PackageConfig config) {
+		packageInfo = new HashMap<String, String>();
+		packageInfo.put(ConstVal.MODULENAME, config.getModuleName());
+		packageInfo.put(ConstVal.ENTITY, joinPackage(config.getParent(), config.getEntity()));
+		packageInfo.put(ConstVal.MAPPER, joinPackage(config.getParent(), config.getMapper()));
+		packageInfo.put(ConstVal.XML, joinPackage(config.getParent(), config.getXml()));
+		packageInfo.put(ConstVal.SERIVCE, joinPackage(config.getParent(), config.getService()));
+		packageInfo.put(ConstVal.SERVICEIMPL, joinPackage(config.getParent(), config.getServiceImpl()));
+		packageInfo.put(ConstVal.CONTROLLER, joinPackage(config.getParent(), config.getController()));
+
+		pathInfo = new HashMap<String, String>();
+		pathInfo.put(ConstVal.ENTITY_PATH, joinPath(outputDir, packageInfo.get(ConstVal.ENTITY)));
+		pathInfo.put(ConstVal.MAPPER_PATH, joinPath(outputDir, packageInfo.get(ConstVal.MAPPER)));
+		pathInfo.put(ConstVal.XML_PATH, joinPath(outputDir, packageInfo.get(ConstVal.XML)));
+		pathInfo.put(ConstVal.SERIVCE_PATH, joinPath(outputDir, packageInfo.get(ConstVal.SERIVCE)));
+		pathInfo.put(ConstVal.SERVICEIMPL_PATH, joinPath(outputDir, packageInfo.get(ConstVal.SERVICEIMPL)));
+		pathInfo.put(ConstVal.CONTROLLER_PATH, joinPath(outputDir, packageInfo.get(ConstVal.CONTROLLER)));
+	}
+
+	/**
+	 * 处理数据源配置
+	 *
+	 * @param config
+	 *            DataSourceConfig
+	 */
+	private void handlerDataSource(DataSourceConfig config) {
+		connection = config.getConn();
+		querySQL = getQuerySQL(config.getDbType());
+	}
+
+	/**
+	 * 处理数据库表 加载数据库表、列、注释相关数据集
+	 *
+	 * @param config
+	 *            StrategyConfig
+	 */
+	private void handlerStrategy(StrategyConfig config) {
+		processTypes(config);
+		tableInfoList = getTablesInfo(config);
+	}
+
+	/**
+	 * 处理superClassName,IdClassType,IdStrategy配置
+	 *
+	 * @param config
+	 *            策略配置
+	 */
+	private void processTypes(StrategyConfig config) {
+		if (StringUtils.isEmpty(config.getSuperServiceClass())) {
+			superServiceClass = ConstVal.SUPERD_SERVICE_CLASS;
+		} else {
+			superServiceClass = config.getSuperServiceClass();
+		}
+		if (StringUtils.isEmpty(config.getSuperServiceImplClass())) {
+			superServiceImplClass = ConstVal.SUPERD_SERVICEIMPL_CLASS;
+		} else {
+			superServiceImplClass = config.getSuperServiceImplClass();
+		}
+		if (StringUtils.isEmpty(config.getSuperMapperClass())) {
+			superMapperClass = ConstVal.SUPERD_MAPPER_CLASS;
+		} else {
+			superMapperClass = config.getSuperMapperClass();
+		}
+		superEntityClass = config.getSuperEntityClass();
+		superControllerClass = config.getSuperControllerClass();
+
+		// ID 策略判断
+		if (config.getIdGenType() == IdStrategy.auto) {
+			idType = IdStrategy.auto.getValue();
+		} else if (config.getIdGenType() == IdStrategy.input) {
+			idType = IdStrategy.input.getValue();
+		} else if (config.getIdGenType() == IdStrategy.uuid) {
+			idType = IdStrategy.uuid.getValue();
+		} else {
+			idType = IdStrategy.id_worker.getValue();
+		}
+	}
+
+	/**
+	 * 处理表对应的类名称
+	 *
+	 * @param tableList
+	 *            表名称
+	 * @param strategy
+	 *            命名策略
+	 * @param tablePrefix
+	 * @return 补充完整信息后的表
+	 */
+	private List<TableInfo> processTable(List<TableInfo> tableList, NamingStrategy strategy, String tablePrefix) {
+		for (TableInfo tableInfo : tableList) {
+			tableInfo.setEntityName(NamingStrategy.capitalFirst(processName(tableInfo.getName(), strategy, tablePrefix)));
+			tableInfo.setMapperName(tableInfo.getEntityName() + ConstVal.MAPPER);
+			tableInfo.setXmlName(tableInfo.getMapperName());
+			tableInfo.setServiceName("I" + tableInfo.getEntityName() + ConstVal.SERIVCE);
+			tableInfo.setServiceImplName(tableInfo.getEntityName() + ConstVal.SERVICEIMPL);
+			tableInfo.setControllerName(tableInfo.getEntityName() + ConstVal.CONTROLLER);
+		}
+		return tableList;
+	}
+
+	/**
+	 * 获取所有的数据库表信息
+	 *
+	 * @return 表信息
+	 */
+	private List<TableInfo> getTablesInfo(StrategyConfig config) {
+		boolean isInclude = (null != config.getInclude() && config.getInclude().length > 0);
+		boolean isExclude = (null != config.getExclude() && config.getExclude().length > 0);
+		if (isInclude && isExclude) {
+			throw new RuntimeException("<strategy> 标签中 <include> 与 <exclude> 只能配置一项!");
+		}
+		List<TableInfo> tableList = new ArrayList<TableInfo>();
+		Set<String> notExistTables = new HashSet<String>();
+		NamingStrategy strategy = config.getNaming();
+		NamingStrategy fieldStrategy = config.getFieldNaming();
+		PreparedStatement pstate = null;
+		try {
+			pstate = connection.prepareStatement(querySQL.getTableCommentsSql());
+			ResultSet results = pstate.executeQuery();
+			while (results.next()) {
+				String tableName = results.getString(querySQL.getTableName());
+				if (StringUtils.isNotEmpty(tableName)) {
+					String tableComment = results.getString(querySQL.getTableComment());
+					TableInfo tableInfo = new TableInfo();
+					if (isInclude) {
+						for (String includeTab : config.getInclude()) {
+							if (includeTab.equalsIgnoreCase(tableName)) {
+								tableInfo.setName(tableName);
+								tableInfo.setComment(tableComment);
+							} else {
+								notExistTables.add(includeTab);
+							}
+						}
+					} else if (isExclude) {
+						for (String excludeTab : config.getExclude()) {
+							if (!excludeTab.equalsIgnoreCase(tableName)) {
+								tableInfo.setName(tableName);
+								tableInfo.setComment(tableComment);
+							} else {
+								notExistTables.add(excludeTab);
+							}
+						}
+					} else {
+						tableInfo.setName(tableName);
+						tableInfo.setComment(tableComment);
+					}
+					if (StringUtils.isNotEmpty(tableInfo.getName())) {
+						List<TableField> fieldList = getListFields(tableInfo.getName(), fieldStrategy);
+						tableInfo.setFields(fieldList);
+						tableList.add(tableInfo);
+					}
+				} else {
+					System.err.println("当前数据库为空!!!");
+				}
+			}
+			// 将已经存在的表移除
+			for (TableInfo tabInfo : tableList) {
+				notExistTables.remove(tabInfo.getName());
+			}
+			if (notExistTables.size() > 0) {
+				System.err.println("表 " + notExistTables + " 在数据库中不存在!!!");
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		} finally {
+			// 释放资源
+			try {
+				if (pstate != null) {
+					pstate.close();
+				}
+				if (connection != null) {
+					connection.close();
+				}
+			} catch (SQLException e) {
+				e.printStackTrace();
+			}
+		}
+		return processTable(tableList, strategy, config.getTablePrefix());
+	}
+
+	/**
+	 * 将字段信息与表信息关联
+	 *
+	 * @param tableName
+	 *            表名称
+	 * @param strategy
+	 *            命名策略
+	 * @return 表信息
+	 */
+	private List<TableField> getListFields(String tableName, NamingStrategy strategy) throws SQLException {
+		boolean havedId = false;
+
+		PreparedStatement pstate = connection.prepareStatement(String.format(querySQL.getTableFieldsSql(), tableName));
+		ResultSet results = pstate.executeQuery();
+
+		List<TableField> fieldList = new ArrayList<TableField>();
+		while (results.next()) {
+			TableField field = new TableField();
+			String key = results.getString(querySQL.getFieldKey());
+			// 避免多重主键设置,目前只取第一个找到ID,并放到list中的索引为0的位置
+			boolean isId = StringUtils.isNotEmpty(key) && key.toUpperCase().equals("PRI");
+			// 处理ID
+			if (isId && !havedId) {
+				field.setKeyFlag(true);
+				havedId = true;
+			} else {
+				field.setKeyFlag(false);
+			}
+			// 处理其它信息
+			field.setName(results.getString(querySQL.getFieldName()));
+			field.setType(results.getString(querySQL.getFieldType()));
+			field.setPropertyName(processName(field.getName(), strategy));
+			field.setPropertyType(processFiledType(field.getType()));
+			field.setComment(results.getString(querySQL.getFieldComment()));
+			fieldList.add(field);
+		}
+		return fieldList;
+	}
+
+	/**
+	 * 连接路径字符串
+	 *
+	 * @param parentDir
+	 *            路径常量字符串
+	 * @param packageName
+	 *            包名
+	 * @return 连接后的路径
+	 */
+	private String joinPath(String parentDir, String packageName) {
+		if (StringUtils.isEmpty(parentDir)) {
+			parentDir = System.getProperty(ConstVal.JAVA_TMPDIR);
+		}
+		if (!StringUtils.endsWith(parentDir, File.separator)) {
+			parentDir += File.separator;
+		}
+		packageName = packageName.replaceAll("\\.", "\\" + File.separator);
+		return parentDir + packageName;
+	}
+
+	/**
+	 * 连接父子包名
+	 *
+	 * @param parent
+	 *            父包名
+	 * @param subPackage
+	 *            子包名
+	 * @return 连接后的包名
+	 */
+	private String joinPackage(String parent, String subPackage) {
+		if (StringUtils.isEmpty(parent)) {
+			return subPackage;
+		}
+		return parent + "." + subPackage;
+	}
+
+	/**
+	 * 处理字段类型
+	 *
+	 * @return 转换成JAVA包装类型
+	 */
+	private String processFiledType(String type) {
+		if (QuerySQL.MYSQL == querySQL) {
+			return processMySqlType(type);
+		} else if (QuerySQL.ORACLE == querySQL) {
+			return processOracleType(type);
+		}
+		return null;
+	}
+
+	/**
+	 * 处理字段名称
+	 *
+	 * @return 根据策略返回处理后的名称
+	 */
+	private String processName(String name, NamingStrategy strategy) {
+		return processName(name, strategy, null);
+	}
+
+	/**
+	 * 处理字段名称
+	 * 
+	 * @param name
+	 * @param strategy
+	 * @param tablePrefix
+	 * @return 根据策略返回处理后的名称
+	 */
+	private String processName(String name, NamingStrategy strategy, String tablePrefix) {
+		String propertyName = "";
+		if (strategy == NamingStrategy.remove_prefix_and_camel) {
+			propertyName = NamingStrategy.removePrefixAndCamel(name, tablePrefix);
+		} else if (strategy == NamingStrategy.underline_to_camel) {
+			propertyName = NamingStrategy.underlineToCamel(name);
+		} else if (strategy == NamingStrategy.remove_prefix) {
+			propertyName = NamingStrategy.removePrefix(name, tablePrefix);
+		} else {
+			propertyName = name;
+		}
+		return propertyName;
+	}
+
+	/**
+	 * MYSQL字段类型转换
+	 *
+	 * @param type
+	 *            字段类型
+	 * @return JAVA类型
+	 */
+	private String processMySqlType(String type) {
+		String t = type.toLowerCase();
+		if (t.contains("char") || t.contains("text")) {
+			return "String";
+		} else if (t.contains("bigint")) {
+			return "Long";
+		} else if (t.contains("int")) {
+			return "Integer";
+		} else if (t.contains("date") || t.contains("time") || t.contains("year")) {
+			return "Date";
+		} else if (t.contains("text")) {
+			return "String";
+		} else if (t.contains("bit")) {
+			return "Boolean";
+		} else if (t.contains("decimal")) {
+			return "BigDecimal";
+		} else if (t.contains("blob")) {
+			return "byte[]";
+		} else if (t.contains("float")) {
+			return "Float";
+		} else if (t.contains("double")) {
+			return "Double";
+		} else if (t.contains("json") || t.contains("enum")) {
+			return "String";
+		}
+		return "String";
+	}
+
+	/**
+	 * ORACLE字段类型转换
+	 *
+	 * @param type
+	 *            字段类型
+	 * @return JAVA类型
+	 */
+	private String processOracleType(String type) {
+		String t = type.toUpperCase();
+		if (t.contains("CHAR")) {
+			return "String";
+		} else if (t.contains("DATE") || t.contains("TIMESTAMP")) {
+			return "Date";
+		} else if (t.contains("NUMBER")) {
+			if (t.matches("NUMBER\\(+\\d{1}+\\)")) {
+				return "Integer";
+			} else if (t.matches("NUMBER\\(+\\d{2}+\\)")) {
+				return "Long";
+			}
+			return "Double";
+		} else if (t.contains("FLOAT")) {
+			return "Float";
+		} else if (t.contains("BLOB")) {
+			return "Object";
+		} else if (t.contains("RAW")) {
+			return "byte[]";
+		}
+		return "String";
+	}
+
+	/**
+	 * 获取当前的SQL类型
+	 *
+	 * @return DB类型
+	 */
+	private QuerySQL getQuerySQL(DbType dbType) {
+		for (QuerySQL qs : QuerySQL.values()) {
+			if (qs.getDbType().equals(dbType.getValue())) {
+				return qs;
+			}
+		}
+		return QuerySQL.MYSQL;
+	}
+
+}

+ 85 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/po/TableField.java

@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config.po;
+
+/**
+ * Developer:YangHu Date:2016-9-3 Describe: 字段信息
+ */
+public class TableField {
+	private boolean keyFlag;
+	private String name;
+	private String type;
+	private String propertyName;
+	private String propertyType;
+	private String comment;
+
+	public boolean isKeyFlag() {
+		return keyFlag;
+	}
+
+	public void setKeyFlag(boolean keyFlag) {
+		this.keyFlag = keyFlag;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getPropertyName() {
+		return propertyName;
+	}
+
+	public void setPropertyName(String propertyName) {
+		this.propertyName = propertyName;
+	}
+
+	public String getPropertyType() {
+		return propertyType;
+	}
+
+	public void setPropertyType(String propertyType) {
+		this.propertyType = propertyType;
+	}
+
+	public String getComment() {
+		return comment;
+	}
+
+	public void setComment(String comment) {
+		this.comment = comment;
+	}
+
+	public boolean isConvert() {
+		return !name.equals(propertyName);
+	}
+
+	public String getCapitalName() {
+		return propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+	}
+
+}

+ 166 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/po/TableInfo.java

@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config.po;
+
+import java.util.List;
+
+import com.baomidou.mybatisplus.toolkit.StringUtils;
+
+/**
+ * 表信息,关联到当前字段信息
+ *
+ * @author YangHu
+ * @since 2016/8/30
+ */
+public class TableInfo {
+
+	private String name;
+	private String comment;
+
+	private String entityName;
+	private String mapperName;
+	private String xmlName;
+	private String serviceName;
+	private String serviceImplName;
+	private String controllerName;
+
+	private List<TableField> fields;
+	private String fieldNames;
+	private boolean hasDate;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getComment() {
+		return comment;
+	}
+
+	public void setComment(String comment) {
+		this.comment = comment;
+	}
+
+	public String getEntityName() {
+		return entityName;
+	}
+
+	public void setEntityName(String entityName) {
+		this.entityName = entityName;
+	}
+
+	public String getMapperName() {
+		return mapperName;
+	}
+
+	public void setMapperName(String mapperName) {
+		this.mapperName = mapperName;
+	}
+
+	public String getXmlName() {
+		return xmlName;
+	}
+
+	public void setXmlName(String xmlName) {
+		this.xmlName = xmlName;
+	}
+
+	public String getServiceName() {
+		return serviceName;
+	}
+
+	public void setServiceName(String serviceName) {
+		this.serviceName = serviceName;
+	}
+
+	public String getServiceImplName() {
+		return serviceImplName;
+	}
+
+	public void setServiceImplName(String serviceImplName) {
+		this.serviceImplName = serviceImplName;
+	}
+
+	public String getControllerName() {
+		return controllerName;
+	}
+
+	public void setControllerName(String controllerName) {
+		this.controllerName = controllerName;
+	}
+
+	public List<TableField> getFields() {
+		return fields;
+	}
+
+	public void setFields(List<TableField> fields) {
+		this.fields = fields;
+	}
+
+	/**
+	 * 转换filed实体为xmlmapper中的basecolumn字符串信息
+	 *
+	 * @return
+	 */
+	public String getFieldNames() {
+		if (StringUtils.isEmpty(fieldNames)) {
+			StringBuilder names = new StringBuilder();
+			for (int i = 0; i < fields.size(); i++) {
+				TableField fd = fields.get(i);
+				if (i == fields.size() - 1) {
+					names.append(cov2col(fd));
+				} else {
+					names.append(cov2col(fd)).append(", ");
+				}
+			}
+			fieldNames = names.toString();
+		}
+		return fieldNames;
+	}
+
+	/**
+	 * 判断字段中是否包含日期类型
+	 *
+	 * @return 是否
+	 */
+	public boolean isHasDate() {
+		for (TableField fieldInfo : fields) {
+			if (fieldInfo.getPropertyType().equals("Date")) {
+				hasDate = true;
+				break;
+			}
+		}
+		return hasDate;
+	}
+
+	/**
+	 * mapper xml中的字字段添加as
+	 *
+	 * @param field
+	 *            字段实体
+	 * @return 转换后的信息
+	 */
+	private String cov2col(TableField field) {
+		if (null != field) {
+			return field.isConvert() ? field.getName() + " AS " + field.getPropertyName() : field.getName();
+		}
+		return StringUtils.EMPTY;
+	}
+
+}

+ 37 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/DbType.java

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config.rules;
+
+/**
+ * 数据库类型定义
+ *
+ * @author YangHu
+ * @since 2016/8/30
+ */
+public enum DbType {
+
+	MYSQL("myslq"), ORACLE("oracle");
+
+	private String value;
+
+	DbType(String value) {
+		this.value = value;
+	}
+
+	public String getValue() {
+		return value;
+	}
+}

+ 37 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/IdStrategy.java

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config.rules;
+
+/**
+ * ID生成策略
+ *
+ * @author YangHu
+ * @since 2016/8/30
+ */
+public enum IdStrategy {
+	auto("AUTO"), id_worker("ID_WORKER"), uuid("UUID"), input("INPUT");
+
+	private String value;
+
+	IdStrategy(String value) {
+		this.value = value;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+}

+ 139 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/NamingStrategy.java

@@ -0,0 +1,139 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config.rules;
+
+import com.baomidou.mybatisplus.generator.config.ConstVal;
+import com.baomidou.mybatisplus.toolkit.StringUtils;
+
+/**
+ * 从数据库表到文件的命名策略
+ *
+ * @author YangHu, tangguo
+ * @since 2016/8/30
+ */
+public enum NamingStrategy {
+	/**
+	 * 不做任何改变,原样输出
+	 */
+	nochange,
+	/**
+	 * 下划线转驼峰命名
+	 */
+	underline_to_camel,
+	/**
+	 * 仅去掉前缀
+	 */
+	remove_prefix,
+	/**
+	 * 去掉前缀并且转驼峰
+	 */
+	remove_prefix_and_camel;
+
+	public static String underlineToCamel(String name) {
+		// 快速检查
+		if (StringUtils.isEmpty(name)) {
+			// 没必要转换
+			return "";
+		}
+		StringBuilder result = new StringBuilder();
+		// 用下划线将原始字符串分割
+		String camels[] = name.toLowerCase().split(ConstVal.UNDERLINE);
+		for (String camel : camels) {
+			// 跳过原始字符串中开头、结尾的下换线或双重下划线
+			if (StringUtils.isEmpty(camel)) {
+				continue;
+			}
+			// 处理真正的驼峰片段
+			if (result.length() == 0) {
+				// 第一个驼峰片段,全部字母都小写
+				result.append(camel);
+			} else {
+				// 其他的驼峰片段,首字母大写
+				result.append(capitalFirst(camel));
+			}
+		}
+		return result.toString();
+	}
+
+	/**
+	 * 去掉下划线前缀
+	 *
+	 * @param name
+	 * @return
+	 */
+	public static String removePrefix(String name) {
+		if (StringUtils.isEmpty(name)) {
+			return "";
+		}
+		int idx = name.indexOf(ConstVal.UNDERLINE);
+		if (idx == -1) {
+			return name;
+		}
+		return name.substring(idx + 1);
+	}
+
+	/**
+	 * 去掉指定的前缀
+	 * 
+	 * @param name
+	 * @param prefix
+	 * @return
+	 */
+	public static String removePrefix(String name, String prefix) {
+		if (StringUtils.isEmpty(name)) {
+			return "";
+		}
+		int idx = name.indexOf(ConstVal.UNDERLINE);
+		if (prefix != null && !"".equals(prefix.trim())) {
+			if (name.toLowerCase().matches("^" + prefix.toLowerCase() + ".*")) { // 判断是否有匹配的前缀,然后截取前缀
+				idx = prefix.length() - 1;
+			}
+		}
+		if (idx == -1) {
+			return name;
+		}
+		return name.substring(idx + 1);
+	}
+
+	/**
+	 * 去掉下划线前缀且将后半部分转成驼峰格式
+	 *
+	 * @param name
+	 * @param tablePrefix
+	 * @return
+	 */
+	public static String removePrefixAndCamel(String name, String tablePrefix) {
+		return underlineToCamel(removePrefix(name, tablePrefix));
+	}
+
+	/**
+	 * 实体首字母大写
+	 *
+	 * @param name
+	 *            待转换的字符串
+	 * @return 转换后的字符串
+	 */
+	public static String capitalFirst(String name) {
+		if (StringUtils.isNotEmpty(name)) {
+			// return name.substring(0, 1).toUpperCase() + name.substring(1);
+			char[] array = name.toCharArray();
+			array[0] -= 32;
+			return String.valueOf(array);
+		}
+		return "";
+	}
+
+}

+ 111 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/generator/config/rules/QuerySQL.java

@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2011-2020, hubin (jobob@qq.com).
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.baomidou.mybatisplus.generator.config.rules;
+
+/**
+ * <p>
+ * 表数据查询
+ * </p>
+ *
+ * @author hubin, tangguo
+ * @since 2016-04-25
+ */
+public enum QuerySQL {
+    MYSQL("mysql", "show tables", "show table status",
+            "show full fields from %s",
+            "NAME", "COMMENT", "FIELD", "TYPE", "COMMENT", "KEY"),
+
+    ORACLE("oracle", "SELECT * FROM USER_TABLES", "SELECT * FROM USER_TAB_COMMENTS",
+            "SELECT A.COLUMN_NAME, CASE WHEN A.DATA_TYPE='NUMBER' THEN " +
+            "(CASE WHEN A.DATA_PRECISION IS NULL THEN A.DATA_TYPE " +
+            "WHEN NVL(A.DATA_SCALE, 0) > 0 THEN A.DATA_TYPE||'('||A.DATA_PRECISION||','||A.DATA_SCALE||')' " +
+            "ELSE A.DATA_TYPE||'('||A.DATA_PRECISION||')' END) " +
+            "ELSE A.DATA_TYPE END DATA_TYPE, B.COMMENTS,DECODE(C.POSITION, '1', 'PRI') KEY " +
+            "FROM USER_TAB_COLUMNS A INNER JOIN USER_COL_COMMENTS B ON A.TABLE_NAME = B.TABLE_NAME" +
+            " AND A.COLUMN_NAME = B.COLUMN_NAME LEFT JOIN USER_CONSTRAINTS D " +
+            "ON D.TABLE_NAME = A.TABLE_NAME AND D.CONSTRAINT_TYPE = 'P' " +
+            "LEFT JOIN USER_CONS_COLUMNS C ON C.CONSTRAINT_NAME = D.CONSTRAINT_NAME " +
+            "AND C.COLUMN_NAME=A.COLUMN_NAME WHERE A.TABLE_NAME = '%s' ",
+            "TABLE_NAME", "COMMENTS", "COLUMN_NAME", "DATA_TYPE", "COMMENTS", "KEY");
+
+    private final String dbType;
+    private final String tablesSql;
+    private final String tableCommentsSql;
+    private final String tableFieldsSql;
+    private final String tableName;
+    private final String tableComment;
+    private final String fieldName;
+    private final String fieldType;
+    private final String fieldComment;
+    private final String fieldKey;
+
+
+    QuerySQL(final String dbType, final String tablesSql, final String tableCommentsSql,
+             final String tableFieldsSql, final String tableName, final String tableComment, final String fieldName,
+             final String fieldType, final String fieldComment, final String fieldKey) {
+        this.dbType = dbType;
+        this.tablesSql = tablesSql;
+        this.tableCommentsSql = tableCommentsSql;
+        this.tableFieldsSql = tableFieldsSql;
+        this.tableName = tableName;
+        this.tableComment = tableComment;
+        this.fieldName = fieldName;
+        this.fieldType = fieldType;
+        this.fieldComment = fieldComment;
+        this.fieldKey = fieldKey;
+    }
+
+    public String getDbType() {
+        return dbType;
+    }
+
+    public String getTablesSql() {
+        return tablesSql;
+    }
+
+    public String getTableCommentsSql() {
+        return tableCommentsSql;
+    }
+
+    public String getTableFieldsSql() {
+        return tableFieldsSql;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public String getTableComment() {
+        return tableComment;
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    public String getFieldType() {
+        return fieldType;
+    }
+
+    public String getFieldComment() {
+        return fieldComment;
+    }
+
+    public String getFieldKey() {
+        return fieldKey;
+    }
+
+}

+ 77 - 0
mybatis-plus/src/main/java/com/baomidou/mybatisplus/toolkit/StringUtils.java

@@ -29,6 +29,8 @@ import java.util.regex.Pattern;
  * @Date 2016-08-18
  */
 public class StringUtils {
+	
+	public static final String EMPTY = "";
 
 	/**
 	 * 下划线字符
@@ -316,4 +318,79 @@ public class StringUtils {
 		return !checkValNotNull(object);
 	}
 
+    // endsWith
+    //-----------------------------------------------------------------------
+
+    /**
+     * <p>Check if a String ends with a specified suffix.</p>
+     *
+     * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
+     * references are considered to be equal. The comparison is case sensitive.</p>
+     *
+     * <pre>
+     * StringUtils.endsWith(null, null)      = true
+     * StringUtils.endsWith(null, "abcdef")  = false
+     * StringUtils.endsWith("def", null)     = false
+     * StringUtils.endsWith("def", "abcdef") = true
+     * StringUtils.endsWith("def", "ABCDEF") = false
+     * </pre>
+     *
+     * @see java.lang.String#endsWith(String)
+     * @param str  the String to check, may be null
+     * @param suffix the suffix to find, may be null
+     * @return <code>true</code> if the String ends with the suffix, case sensitive, or
+     *  both <code>null</code>
+     * @since 2.4
+     */
+    public static boolean endsWith(String str, String suffix) {
+        return endsWith(str, suffix, false);
+    }
+
+    /**
+     * <p>Case insensitive check if a String ends with a specified suffix.</p>
+     *
+     * <p><code>null</code>s are handled without exceptions. Two <code>null</code>
+     * references are considered to be equal. The comparison is case insensitive.</p>
+     *
+     * <pre>
+     * StringUtils.endsWithIgnoreCase(null, null)      = true
+     * StringUtils.endsWithIgnoreCase(null, "abcdef")  = false
+     * StringUtils.endsWithIgnoreCase("def", null)     = false
+     * StringUtils.endsWithIgnoreCase("def", "abcdef") = true
+     * StringUtils.endsWithIgnoreCase("def", "ABCDEF") = false
+     * </pre>
+     *
+     * @see java.lang.String#endsWith(String)
+     * @param str  the String to check, may be null
+     * @param suffix the suffix to find, may be null
+     * @return <code>true</code> if the String ends with the suffix, case insensitive, or
+     *  both <code>null</code>
+     * @since 2.4
+     */
+    public static boolean endsWithIgnoreCase(String str, String suffix) {
+        return endsWith(str, suffix, true);
+    }
+
+    /**
+     * <p>Check if a String ends with a specified suffix (optionally case insensitive).</p>
+     *
+     * @see java.lang.String#endsWith(String)
+     * @param str  the String to check, may be null
+     * @param suffix the suffix to find, may be null
+     * @param ignoreCase inidicates whether the compare should ignore case
+     *  (case insensitive) or not.
+     * @return <code>true</code> if the String starts with the prefix or
+     *  both <code>null</code>
+     */
+    private static boolean endsWith(String str, String suffix, boolean ignoreCase) {
+        if (str == null || suffix == null) {
+            return (str == null && suffix == null);
+        }
+        if (suffix.length() > str.length()) {
+            return false;
+        }
+        int strOffset = str.length() - suffix.length();
+        return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length());
+    }
+
 }