【MyBatis】MyBatis自动生成代码查询之爬坑记
前言
项目使用SSM框架搭建Web后台服务,前台后使用
restful api
,后台使用MyBatisGenerator自动生成代码,在前台使用关键字进行查询时,遇到了一些很宝贵的坑,现记录如下。为展示所遇问题,将项目进行了精简。
项目框架
后台框架
后台框架选型为Spring + SpringMVC + Mybatis + Jetty
,其中使用MyBatisGenerator
创建代码,Jetty
为内嵌的Web服务器。
项目代码
代码已上传至github
项目介绍
数据准备
创建库ssm
和表users
,其中创建表users
的SQL
如下。
CREATE TABLE `users` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`address` varchar(32) DEFAULT NULL,
`hobby` varchar(64) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
insert into users(name, address, hobby, content) values("leesf", "hubei", "sport, race", "he is a boy");
insert into users(name, address, hobby, content) values("dyd", "hubei", "painting, reading", "she is a girl");
自动生成代码框架
使用MyBatisGenerator自动生成相应代码,其源码如下。
package com.leesf;
import org.junit.Test;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class MybatisGenerator {
@Test public void generator() throws Exception {
List<String> warnings = new ArrayList<String>();
File configFile = new File(
"F:/01_Code/01_Idea/ssm-master/src/test/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(true);
MyBatisGenerator myBatisGenerator =
new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
其中generatorConfig.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> 元素用于指定生成一组对象的环境。 子元素用于指定要连接到的数据库、 要生成对象的类型和要内省的表 -->
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=UTF-8"
userId="root"
password="">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- targetProject:生成PO类的位置
注意对于targetProject:In other environments(other than Eclipse),
this value should be an existing directory on the local file system.
即对于非eclipse项目需要指定绝对路径
-->
<javaModelGenerator targetPackage="com.leesf.po"
targetProject="F:/01_Code/01_Idea/ssm-master/src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.leesf.mapper"
targetProject="F:/01_Code/01_Idea/ssm-master/src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.leesf.mapper"
targetProject="F:/01_Code/01_Idea/ssm-master/src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="users"></table>
<!-- <table schema="" tableName="sys_user"></table>
<table schema="" tableName="sys_role"></table>
<table schema="" tableName="sys_permission"></table>
<table schema="" tableName="sys_user_role"></table>
<table schema="" tableName="sys_role_permission"></table> -->
<!-- 有些表的字段需要指定java类型
<table schema="" tableName="">
<columnOverride column="" javaType="" />
</table> -->
</context>
</generatorConfiguration>
WebServer
WebServer
为Web容器,其源码如下。
package com.leesf.main;
import java.net.UnknownHostException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebServer {
public static final String CONTEXT = "/";
private static final Logger LOG = LoggerFactory.getLogger(WebServer.class);
private static final String DEFAULT_WEBAPP_PATH = "webapps/";
private Server server;
private int port;
public WebServer() {
}
public Server createServerInSource() throws UnknownHostException {
port = 8081;
server = new Server();
server.setStopAtShutdown(true);
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(port);
connector.setReuseAddress(false);
connector.setAcceptQueueSize(50);
connector.setAcceptors(2);
connector.setThreadPool(
new ExecutorThreadPool(20,
40, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(
16, false)));
connector.setLowResourcesMaxIdleTime(3000);
connector.setReuseAddress(true);
connector.setRequestBufferSize(
16 * 1024);
connector.setRequestHeaderSize(
8 * 1024);
server.setConnectors(new Connector[] { connector });
String basePath = "src/main/webapps";
if (StringUtils.isEmpty(basePath)) {
basePath = DEFAULT_WEBAPP_PATH;
}
WebAppContext webContext = new WebAppContext(basePath, CONTEXT);
webContext.setContextPath(CONTEXT);
webContext.setDescriptor(basePath + "/WEB-INF/web.xml");
System.out.println("-------------web.xml path is " + webContext.getDescriptor()
+ "--------------");
webContext.setResourceBase(basePath);
webContext.setClassLoader(Thread.currentThread().getContextClassLoader());
server.setHandler(webContext);
return server;
}
public void start() throws Exception {
if (server == null) {
createServerInSource();
}
if (server != null) {
server.start();
LOG.info("WebServer has started at port:" + port);
}
}
public void stop() throws Exception {
if (server != null) {
server.stop();
}
}
public static void main(String[] args) throws Exception {
WebServer webServer = new WebServer();
webServer.start();
}
}
使用内嵌Jetty
方式提供Web服务,只做演示,其中参数并未进行调优处理。
Controller
只存在UserController
,其源码如下。
package com.leesf.controller;
import com.leesf.po.Users;
import com.leesf.service.UserService;
import com.leesf.utils.ResultUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStreamWriter;
import java.util.List;
@Controller @RequestMapping(value = "/users")
public class UserController {
@Autowired UserService userService;
@ResponseBody @RequestMapping(value = "/listUsers", method = {
RequestMethod.POST, RequestMethod.GET }) public void listUsers(
HttpServletRequest request, HttpServletResponse response,
@RequestParam(required = false) String name,
@RequestParam(required = false) String key) throws Exception {
System.out.println("xxxxxx");
OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream());
List<Users> users = userService.getUsers(name, key);
ResultUtils.resultSuccess(users, out);
}
}
可以根据用户名字和关键字查询用户。
Service
UserServiceImp
为UserService
的实现类。
- 根据用户名查询指定记录
其源码如下。
package com.leesf.service.impl;
import com.leesf.mapper.UsersMapper;
import com.leesf.po.Users;
import com.leesf.po.UsersExample;
import com.leesf.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service("userService") public class UserServiceImp implements UserService {
@Autowired UsersMapper usersMapper;
private Logger LOG = LoggerFactory.getLogger(this.getClass());
public List<Users> getUsers(String name, String key) {
UsersExample usersExample = new UsersExample();
usersExample.createCriteria().andNameLike(name);
List<Users> users = usersMapper.selectByExampleWithBLOBs(usersExample);
return users;
}
}
可以看到,该Service
根据name
查找匹配name
的记录,启动WebServer
,访问http://localhost:8081/users/listUsers?name=sport
,查看编译器运行的信息,发现构造了如下SQL语句,
select id, name, address, hobby , content from users WHERE ( name like ? )
。
url访问结果如下:
{
result_code: "0",
result_msg: "Succeed!",
result_content: [{
id: 1,
name: "leesf",
address: "hubei",
hobby: "sport, race",
content: "he is a boy"
}]
}
- 根据用户名和关键字查询指定记录
其源码如下
package com.leesf.service.impl;
import com.leesf.mapper.UsersMapper;
import com.leesf.po.Users;
import com.leesf.po.UsersExample;
import com.leesf.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service("userService") public class UserServiceImp implements UserService {
@Autowired UsersMapper usersMapper;
private Logger LOG = LoggerFactory.getLogger(this.getClass());
public List<Users> getUsers(String name, String key) {
UsersExample usersExample = new UsersExample();
if (StringUtils.isNotEmpty(key)) {
usersExample.or().andAddressLike(key);
usersExample.or().andHobbyLike(key);
}
if (StringUtils.isNotEmpty(name)) {
if (usersExample.getOredCriteria().size() == 0){
usersExample.createCriteria();
}
usersExample.getOredCriteria().get(0).andNameEqualTo(name);
}
List<Users> users = usersMapper.selectByExampleWithBLOBs(usersExample);
return users;
}
}
key
可以匹配address
或者hobby
,而name
必须要匹配name,也就是希望构造这样的一条查询SQLselect * from users where (address like "%sport%" or hobby like "%sport%") and name = "leesf"
。
启动WebServer
,访问http://localhost:8081/users/listUsers?key=sport&name=leesf
。
结果如下
{
result_code: "0",
result_msg: "Succeed!",
result_content: []
}
可以看到访问结果中result_content
为空,查看运行日志,发现如下SQL语句
select id, name, address, hobby , content from users WHERE ( address like ? and name = ? ) or( hobby like ? )
程序实际构造的SQL并非之前所想的那样,此时查阅资料,发现MyBatis自动生成代码还不支持纯生的(a or b) and c
这样的SQL
语句,需要进行等价变化,即(a or b) and c = (a and c) or (b and c)
,具体可查看如下链接,按照这样的思路进行如下修改。
public List<Users> getUsers(String name, String key) {
UsersExample usersExample = new UsersExample();
if (StringUtils.isNotEmpty(key)) {
usersExample.or().andAddressLike(key);
usersExample.or().andHobbyLike(key);
}
if (StringUtils.isNotEmpty(name)){
if (usersExample.getOredCriteria().size() == 0){
usersExample.createCriteria();
}
usersExample.getOredCriteria().get(0).andNameEqualTo(name);
usersExample.getOredCriteria().get(1).andNameEqualTo(name);
}
List<Users> users = usersMapper.selectByExampleWithBLOBs(usersExample);
return users;
}
此时,再次查询,发现还是没有结果,查看运行时信息发现如下SQL
。
select id, name, address, hobby , content from users WHERE ( address like ? and name = ? ) or( hobby like ? and name = ? )
,看似SQL
语句没有任何问题,但是就是出不来结果,like
和前面也是一样的,百思不得其解,继续查阅资料也无解,后面尝试对like
添加%
处理,修改如下。
public List<Users> getUsers(String name, String key) {
UsersExample usersExample = new UsersExample();
if (StringUtils.isNotEmpty(key)) {
usersExample.or().andAddressLike("%" + key + "%");
usersExample.or().andHobbyLike("%" + key + "%");
}
if (StringUtils.isNotEmpty(name)) {
if (usersExample.getOredCriteria().size() == 0){
usersExample.createCriteria();
}
usersExample.getOredCriteria().get(0).andNameEqualTo(name);
usersExample.getOredCriteria().get(1).andNameEqualTo(name);
}
List<Users> users = usersMapper.selectByExampleWithBLOBs(usersExample);
return users;
}
重新运行并访问url,得到如下结果:
{
result_code: "0",
result_msg: "Succeed!",
result_content: [{
id: 1,
name: "leesf",
address: "hubei",
hobby: "sport, race",
content: "he is a boy"
}]
}
在配合or使用的情况下,like必须显示添加%才能生效,虽然按照这种等价的方式可以进行处理,还是有些麻烦,特别是当or字段非常多的时候,处理比较麻烦,如(a or b or c or d) and e
,其需要处理成(a and e) or (b and e) or (c and e) or (d and e)
,继续查阅资料,看是否有更为简便的写法,在stackoverflow上发现有这样的处理方式,进行如下改造。
public List<Users> getUsers(String name, String key) {
UsersExample usersExample = new UsersExample();
if (StringUtils.isNotEmpty(key)) {
Map<String, String> maps = new HashMap<String, String>();
maps.put("address", key);
maps.put("hobby", key);
usersExample.createCriteria().multiColumnOrLike(maps);
}
if (StringUtils.isNotEmpty(name)) {
if (usersExample.getOredCriteria().size() == 0){
usersExample.createCriteria();
}
usersExample.getOredCriteria().get(0).andNameEqualTo(name);
}
List<Users> users = usersMapper.selectByExampleWithBLOBs(usersExample);
return users;
}
修改UserExample.java
的Criteria
如下。
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
public Criteria multiColumnOrLike(Map<String, String> maps) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("( ");
for (Map.Entry<String, String> entry : maps.entrySet()) {
stringBuffer.append(entry.getKey());
stringBuffer.append(" like ");
stringBuffer.append("\"%");
stringBuffer.append(entry.getValue());
stringBuffer.append("%\"");
stringBuffer.append(" or ");
}
int index = stringBuffer.lastIndexOf("or");
stringBuffer.delete(index, stringBuffer.length());
stringBuffer.append(")");
addCriterion(stringBuffer.toString());
return this;
}
}
再次启动运行,结果如下。
{
result_code: "0",
result_msg: "Succeed!",
result_content: [{
id: 1,
name: "leesf",
address: "hubei",
hobby: "sport, race",
content: "he is a boy"
}]
}
总结
可以看到使用MyBatisGenerator自动生成代码时,需要注意如下可能出现的坑。
- 当进行(a or b) and c查询时,可通过转变为(a and c) or (b and c)方式进行查询,但个人认为更好的方法是修改Example文件,进行定制化的查询处理。
- 单独使用andxxxLike时,不需要添加"%"处理,而配合
or
时,必须添加"%"才行。
【MyBatis】MyBatis自动生成代码查询之爬坑记的更多相关文章
- SpringBoot 添加mybatis generator 自动生成代码插件
自动生成数据层代码,提高开发效率 1.pom添加插件,并指定配置文件路径 <!-- mybatis generator 自动生成代码插件 --> <plugin> <gr ...
- SpringBoot入门篇--整合mybatis+generator自动生成代码+druid连接池+PageHelper分页插件
原文链接 我们这一篇博客讲的是如何整合Springboot和Mybatis框架,然后使用generator自动生成mapper,pojo等文件.然后再使用阿里巴巴提供的开源连接池druid,这个连接池 ...
- IDEA Maven Mybatis generator 自动生成代码(实例讲解)(转)
IDEA Maven Mybatis generator 自动生成代码(实例讲解) MyBatis Generator • 简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的 ...
- (转)MyBatis框架的学习(七)——MyBatis逆向工程自动生成代码
http://blog.csdn.net/yerenyuan_pku/article/details/71909325 什么是逆向工程 MyBatis的一个主要的特点就是需要程序员自己编写sql,那么 ...
- MyBatis框架之mybatis逆向工程自动生成代码
http://www.jb51.net/article/82062.htm Mybatis属于半自动ORM,在使用这个框架中,工作量最大的就是书写Mapping的映射文件,由于手动书写很容易出错,我们 ...
- idea中mybatis generator自动生成代码配置 数据库是sqlserver
好长时间没有写博客了,最近公司要用java语言,开始学习java,属于初学者,今天主要记录一下mybatis generator自动生成代码,首先在如下图的目录中新建两个文件,如下图 generato ...
- IDEA Maven Mybatis generator 自动生成代码
IDEA Maven Mybatis generator 自动生成代码 一.安装配置maven以及在Idea中配置maven 安装过程步骤可以看上面的博文,里面介绍得很详细. 二.建数据表 DROP ...
- 使用mybatis插件自动生成代码以及问题处理
1.pom.xml中加入依赖插件 <!-- mybatis generator 自动生成代码插件 --> <plugin> <groupId>org.mybatis ...
- SpringBoot+Mybatis+MySql 自动生成代码 自动分页
一.配置文件 <!-- 通用mapper --> <dependency> <groupId>tk.mybatis</groupId> <arti ...
随机推荐
- InputStream中通过mark和reset方法重复利用缓存
通过缓存InputStream可重复利用一个InputStream,但是要缓存一整个InputStream内存压力可能是比较大的.如果第一次读取InputStream是用来判断文件流类型,文件编码等用 ...
- 判断json是否包含了每个键 PHP
(1)可以用array_key_exists去判断object对象或array数组中是否含有某个键: (2)不可以用isset去判断判断object对象或array数组中是否含有某个键 $decode ...
- windows转mac-开发环境搭建(一):需要搭建的环境及安装的工具
作为一个java后端开发者来说,随着项目的增加,前段时间用windows真是受尽折磨,电脑卡到不行,在我们开发部技术大佬的一再安利之下,狠下心选了个17年13寸带touch bar的MacBook P ...
- 模板语言变量,js变量,js自执行函数之前嵌套调用
1.模板语言变量 前端html页面中展示 {{ nodeIp }} 2.js变量引用模板语言变量 把模板语言变量传递给js,js去执行页面操作(变量的转换,只适用于字符串) var IP = &quo ...
- 如何更改Ubuntu的root密码
安装Ubuntu系统时,只提示了设定用户密码,该密码可用于普通用户暂时获取root的权限,执行一些需要root权限的操作,而没有要求我们设置root密码,在需要用到root密码时,却想不起来,很尴尬啊 ...
- javascript-深入理解&&和||
先从两个问题看起: 第一个问题 为什么 a && b 返回的是true,b && a 返回的是6 var user = 6; var both = true; cons ...
- 笔记+R︱信用风险建模中神经网络激活函数与感知器简述
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 本笔记源于CDA-DSC课程,由常国珍老师主讲 ...
- Eviews 9.0新功能——估计方法(ARDL、面板自回归、门限回归)
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 9.2 估计功能 eviews9.0下载链接: ...
- 小实验3:实现haproxy的增、删、查
# Author:Alano # -*- conding:utf-8 -*- # 这里有一个问题:为什么手动删除了haproxy_new中的内容,但是执行添加命令的时候依然显示数据已经存在? f = ...
- java创建自定义类的数组
今天在学图论的最小生成树,开始一直在想是用邻接矩阵还是关联矩阵来表示图,但是发现这样都会有好多空间浪费.于是我就自定义一个边的类,里面包含了权值,关联的端点1,端点2,和图的表示字母.发现我想创建11 ...