MyBatis教程 ——走马观花
前言
MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。
用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。
MyBatis 最强大的特性之一就是它的动态语句功能。如果您以前有使用JDBC或者类似框架的经历,您就会明白把SQL语句条件连接在一起是多么的痛苦,要确保不能忘记空格或者不要在columns列后面省略一个逗号等。
MyBatis环境配置搭建项目
上来直接构建一个经典的maven web项目。
前提:
#数据库、表
CREATE DATABASE hello_mybatis;
USE hello_mybatis;
CREATE TABLE `t_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) NOT NULL COMMENT '姓名',
`dept` varchar(254) NOT NULL COMMENT '部门',
`phone` varchar(16) NOT NULL COMMENT '电话',
`height` decimal(10,2) DEFAULT NULL COMMENT '身高',
`create_emp` bigint(20) NOT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_emp` bigint(20) DEFAULT NULL COMMENT '修改人',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';
INSERT INTO `hello_mybatis`.`t_user` (`name`, `dept`, `phone`, `height`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
( '大青山', 'amy empire', '18956563229', '1.85', '1', '2020-03-31 14:17:35', '1', '2020-03-31 14:17:47');
INSERT INTO `hello_mybatis`.`t_user` (`name`, `dept`, `phone`, `height`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
( '池寒枫', 'amy empire', '22056545', '1.83', '1', '2020-03-31 14:17:35', '1', '2020-03-31 14:17:47');
代码示例:
mybatis-config.xml
注意:
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/self/dao/UserMapper
UserMapper.xml
CRUD 的sql在各个对应的Mapper 映射文件上写。
bean,数据库的映射对象实体类。
public class User {
/**
* id
*/
private int id;
/**
* 名字
*/
private String name;
/**
* 部门,帝国
*/
private String dept;
/**
* 联系号码
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private String createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private String modifyTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
//其他省略
}
main方法。
public class HelloTest {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSessionFactory(){
return sqlSessionFactory;
}
public static void main(String[] args) {
SqlSession sqlSession = getSessionFactory().openSession();
try {
//我们通过调用UserMapper.xml的namespace + id(方法名)来完成数据库访问
User user = (User)sqlSession.selectOne("com.self.dao.UserMapper.getUserById", 1);
System.out.println(JSON.toJSONString(user));
} finally {
sqlSession.close();
}
}
}
输出:
{"dept":"amy empire","height":1.75,"id":1,"name":"艾米","phone":"18956563228"}
配置解释:
- 配置文件 mybatis-config.xml 是 mybatis 用来建立 sessionFactory,里面主要包含了数据库连接相关内容,还有 java 类所对应的别名,比如:
这个别名非常重要,在具体的类的映射中,比如:UserMapper.xml 中 resultType 就是对应这个。 - mybatis-config.xml 里面 的
是包含要映射的类(UserMapper.java)的 xml 配置文件。 - 在UserMapper.xml 文件里面主要是定义各种 SQL 语句,以及这些语句的参数,以及要返回的类型等等。
Mybatis接口注解 (项目中一般不使用)
示例:
创建DAO接口
package com.self.dao;
import com.self.bean.User;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Select("SELECT * FROM t_user where name =#{name}")
public User getUserByName(String name);
}
mybatis-config.xml 配置文件不需要配置mappers,转而在sqlSessionFactory.getConfiguration().addMapper(IUser.class);里添加。
User 实体类不变。
测试类
public class HelloTest {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//在sqlSessionFactory中添加Mapper
sqlSessionFactory.getConfiguration().addMapper(UserMapper.class);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSessionFactory(){
return sqlSessionFactory;
}
public static void main(String[] args) {
SqlSession sqlSession = getSessionFactory().openSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserByName("大青山");
System.out.println(JSON.toJSONString(user));
} finally {
sqlSession.close();
}
}
}
Mybatis增删改查(CURD)
示例:
mybatis-config.xml
User 实体类不变。
dao层接口
public interface UserMapper {
public User getUserById(Integer id);
public List getUsers();
public void insert(User user);
public void update(User user);
public void deleteById(Integer id);
}
Mapper 映射文件 : src\main\resources\com\self\dao\UserMapper.xml
insert into t_user(`name`, `dept`, `phone`, `height`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
(#{name},#{dept},#{phone},#{height},#{createEmp},#{createTime},#{modifyEmp},#{modifyTime})
delete from t_user where id = #{id}
update t_user
name = #{name},
dept = #{dept},
phone = #{phone},
height = #{height},
modify_emp = #{modifyEmp},
modify_time = #{modifyTime}
where id = #{id}
测试类
package com.self;
import com.alibaba.fastjson.JSON;
import com.self.bean.User;
import com.self.dao.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
public class HelloTest {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//在sqlSessionFactory中添加Mapper
//sqlSessionFactory.getConfiguration().addMapper(UserMapper.class);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSessionFactory(){
return sqlSessionFactory;
}
public static void main(String[] args) {
SqlSession sqlSession = getSessionFactory().openSession();
try {
//我们通过调用UserMapper.xml的namespace + id(方法名)来完成数据库访问
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setCreateEmp(3L);
user.setCreateTime(new Date());
user.setDept("矮人王国");
user.setHeight(new BigDecimal("1.53"));
user.setModifyEmp(3L);
user.setModifyTime(new Date());
user.setPhone("852-253521");
user.setName("霍恩斯");
userMapper.insert(user);
User user1 = new User();
user1.setCreateEmp(3L);
user1.setCreateTime(new Date());
user1.setDept("矮人王国");
user1.setHeight(new BigDecimal("1.53"));
user1.setModifyEmp(3L);
user1.setModifyTime(new Date());
user1.setPhone("852-253521");
user1.setName("霍恩斯-克隆");
userMapper.insert(user1);
sqlSession.commit();
System.out.println("--------------------insert---------------------");
User user2 = userMapper.getUserById(4);
System.out.println("-----getUserById----"+JSON.toJSONString(user2));
User updateUser = new User();
updateUser.setDept("森林矮人王国");
updateUser.setHeight(new BigDecimal("1.56"));
updateUser.setModifyEmp(6L);
updateUser.setModifyTime(new Date());
updateUser.setPhone("852-253521");
updateUser.setName("霍恩斯");
updateUser.setId(user2.getId());
userMapper.update(updateUser);
sqlSession.commit();
System.out.println("--------------------update---------------------");
List users = userMapper.getUsers();
System.out.println("----before delete-----"+JSON.toJSONString(users));
userMapper.deleteById(5);
sqlSession.commit();
System.out.println("--------------------deleteById---------------------");
List afterUsers = userMapper.getUsers();
System.out.println("----before delete-----"+JSON.toJSONString(afterUsers));
} finally {
sqlSession.close();
}
}
}
输出:
--------------------insert---------------------
-----getUserById----{"dept":"矮人王国","height":1.53,"id":4,"name":"霍恩斯","phone":"852-253521"}
--------------------update---------------------
----before delete-----[{"dept":"amy empire","height":1.75,"id":1,"name":"艾米","phone":"18956563228"},{"dept":"amy empire","height":1.85,"id":2,"name":"大青山","phone":"18956563229"},{"dept":"amy empire","height":1.83,"id":3,"name":"池寒枫","phone":"22056545"},{"dept":"森林矮人王国","height":1.56,"id":4,"name":"霍恩斯","phone":"852-253521"},{"dept":"矮人王国","height":1.53,"id":5,"name":"霍恩斯-克隆","phone":"852-253521"}]
--------------------deleteById---------------------
----before delete-----[{"dept":"amy empire","height":1.75,"id":1,"name":"艾米","phone":"18956563228"},{"dept":"amy empire","height":1.85,"id":2,"name":"大青山","phone":"18956563229"},{"dept":"amy empire","height":1.83,"id":3,"name":"池寒枫","phone":"22056545"},{"dept":"森林矮人王国","height":1.56,"id":4,"name":"霍恩斯","phone":"852-253521"}]
注意:在增加,更改,删除的时候需要调用 session.commit()
来提交事务,这样才会真正对数据库进行操作提交保存,否则操作没有提交到数据中。
注意:
1、如果在mybatis-config.xml没有配置mapper的xml文件就会报下面的UserMapper is not known to the MapperRegistry.
Exception in thread "main" org.apache.ibatis.binding.BindingException: Type interface com.self.dao.UserMapper is not known to the MapperRegistry.
at org.apache.ibatis.binding.MapperRegistry.getMapper
2、如果插入的数据与字段不匹配个数会报Column count doesn't match value count at row 1。
### SQL: insert into t_user(`name`, `dept`, `phone`, `height`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES (?,?,?,?,?,?,?,?,?)
### Cause: java.sql.SQLException: Column count doesn't match value count at row 1
Mybatis表关联一对多
准备:
CREATE TABLE `t_weibo` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` int(10) unsigned NOT NULL COMMENT '用户id',
`title` varchar(254) NOT NULL COMMENT '主题',
`content` text DEFAULT NULL COMMENT '内容',
`create_emp` bigint(20) NOT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_emp` bigint(20) DEFAULT NULL COMMENT '修改人',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT= 1000 DEFAULT CHARSET=utf8 COMMENT='微博帖子表';
INSERT INTO `hello_mybatis`.`t_weibo` ( `user_id`, `title`, `content`, `create_emp`, `create_time`, `modify_emp`, `modify_time`)
VALUES ( '4', '霍恩斯描述', '由于矮人种族的原因,他无法成为龙骑士,但这并不影响他在小佣兵团以及整个人神大战中快速成为超级巨星,擅长谋略尤擅兵法让他成为矮人的异类。', '2', '2020-04-01 13:47:24', '2', '2020-04-01 13:47:31');
INSERT INTO `hello_mybatis`.`t_weibo` ( `user_id`, `title`, `content`, `create_emp`, `create_time`, `modify_emp`, `modify_time`)
VALUES ( '4', '霍恩斯与莹', '艾米、大青山、霍恩斯、池傲天、沙若等佣兵团骨干可以逃脱,那些培养了2年多的人才必然全军覆没。', '2', '2020-04-01 13:47:24', '2', '2020-04-01 13:47:31');
INSERT INTO `hello_mybatis`.`t_weibo` ( `user_id`, `title`, `content`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
( '2', '大青山描述', '史料记载中第一个神圣巨龙使骑士,在所有的文明中,他都是传统骑士准则的典范,正直、善良、忠诚、守信、怜悯等一切人类美德的拥有者。人龙神三人组合中的一人。在神界受到龙神以黄金凤的胁迫,毅然解除与冰系神圣巨龙使泰穆格尔赛的龙骑士契约,解除龙神的威胁,后与光明神同归于尽。', '2', '2020-04-01 13:47:24', '2', '2020-04-01 13:47:31');
实例:
public class User {
/**
* id
*/
private int id;
/**
* 名字
*/
private String name;
/**
* 部门,帝国
*/
private String dept;
/**
* 联系号码
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
private List weibos;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private Date modifyTime;
public List getWeibos() {
return weibos;
}
public void setWeibos(List weibos) {
this.weibos = weibos;
}
//其他省略
}
public class Weibo {
/**
* id
*/
private int id;
/**
* 主题
*/
private String title;
/**
* 内容
*/
private String content;
/**
* 用户信息
*/
private User user;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private Date modifyTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
mybatis-config.xml
UserMapper.xml
package com.self;
import com.alibaba.fastjson.JSON;
import com.self.bean.User;
import com.self.dao.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* Copyright (C), 2015-2020, UCAR.INC
* ClassName: HelloTest
* Author: Rongfeng.Lai
* Date: 2020/3/31 14:42
* Description:
* History:
*
输出:
---------{"dept":"森林矮人王国","id":4,"name":"霍恩斯","phone":"852-253521","weibos":[{"content":"由于矮人种族的原因,他无法成为龙骑士,但这并不影响他在小佣兵团以及整个人神大战中快速成为超级巨星,擅长谋略尤擅兵法让他成为矮人的异类。","id":4,"title":"霍恩斯描述"}]}
当配置resultMap时jdbcType="TEXT"会报错,要配置成jdbcType="VARCHAR"。
报错:
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/self/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving JdbcType. Cause: java.lang.IllegalArgumentException: No enum constant org.apache.ibatis.type.JdbcType.TEXT
Mybatis表关联多对一
实例:
实体类bean不变
UserMapper.xml
测试类
Weibo weibo = sqlSession.selectOne("com.self.dao.UserMapper.getWeiboInfoById", 1000);
System.out.println("---------"+JSON.toJSONString(weibo));
输出:
---------{"content":"史料记载中第一个神圣巨龙使骑士,在所有的文明中,他都是传统骑士准则的典范,正直、善良、忠诚、守信、怜悯等一切人类美德的拥有者。人龙神三人组合中的一人。在神界受到龙神以黄金凤的胁迫,毅然解除与冰系神圣巨龙使泰穆格尔赛的龙骑士契约,解除龙神的威胁,后与光明神同归于尽。","id":2,"title":"大青山描述","user":{"dept":"amy empire","id":2,"name":"大青山"}}
Mybatis 多对多
mybatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作。因此在进行实体类多对多映射表设计时,需要专门建立一个关联对象类对相关实体类的关联关系进行描述(映射表 user_side )。
多对多关系本质上还是一对多关系的延伸,各自是一对多关系的联系,因此变成了多对多的关系,在对单个数据处理时还是一对多关系的处理。
前提准备:
CREATE TABLE `t_side` (
`side_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`side_name` varchar(254) NOT NULL COMMENT '势力名称',
`create_emp` bigint(20) NOT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modify_emp` bigint(20) DEFAULT NULL COMMENT '修改人',
`modify_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`side_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='所属势力表';
INSERT INTO `hello_mybatis`.`t_side` ( `side_name`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
('艾米帝国', '10', '2020-04-01 16:36:25', '10', '2020-04-01 16:36:28');
INSERT INTO `hello_mybatis`.`t_side` ( `side_name`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
('哈米尔王国', '10', '2020-04-01 16:36:25', '10', '2020-04-01 16:36:28');
INSERT INTO `hello_mybatis`.`t_side` ( `side_name`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
('恶魔岛', '10', '2020-04-01 16:36:25', '10', '2020-04-01 16:36:28');
INSERT INTO `hello_mybatis`.`t_side` ( `side_name`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
('精灵王国', '10', '2020-04-01 16:36:25', '10', '2020-04-01 16:36:28');
CREATE TABLE `t_user_side` (
`user_id` int(10) unsigned NOT NULL COMMENT '用户id' ,
`side_id` int(10) unsigned NOT NULL COMMENT '所属势力id'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT ='用户势力映射表';
INSERT INTO `hello_mybatis`.`t_user_side` (`user_id`, `side_id`) VALUES ('1', '100');
INSERT INTO `hello_mybatis`.`t_user_side` (`user_id`, `side_id`) VALUES ('1', '101');
INSERT INTO `hello_mybatis`.`t_user_side` (`user_id`, `side_id`) VALUES ('1', '103');
INSERT INTO `hello_mybatis`.`t_user_side` (`user_id`, `side_id`) VALUES ('3', '100');
INSERT INTO `hello_mybatis`.`t_user_side` (`user_id`, `side_id`) VALUES ('2', '100');
sql错误修改
ALTER TABLE t_user_group RENAME t_user_side;
ALTER TABLE t_user_side CHANGE group_id side_id int(10) unsigned NOT NULL COMMENT '所属势力id';
实例:
bean对象
public class User {
/**
* 成员所属势力群
*/
private List sides;
/**
* id
*/
private int id;
/**
* 名字
*/
private String name;
/**
* 部门,帝国
*/
private String dept;
/**
* 联系号码
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
//private List weibos;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private Date modifyTime;
}
public class Side {
/**
* 所属势力下成员
*/
private List users;
/**
* 所属势力id
*/
private int sideId;
/**
* 所属势力名称
*/
private String sideName;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private Date modifyTime;
}
public class UserSide {
/**
* 用户id
*/
private int userId;
/**
* 势力id
*/
private int sideId;
}
mybatis-config.xml
UserMapper.xml
SideMapper.xml
insert into t_side(`side_name`, `create_emp`, `create_time`, `modify_emp`, `modify_time`) VALUES
(#{sideName},#{createEmp},#{createTime},#{modifyEmp},#{modifyTime})
UserSideMapper.xml
INSERT INTO t_user_side (`user_id`, `group_id`) VALUES (#{userId},#{sideId})
//测试类代码
UserSideMapper us = sqlSession.getMapper(UserSideMapper.class);
List sides = us.getSidesByUserId(1);
StringBuilder sb = new StringBuilder();
for (Side side : sides) {
sb.append(side.getSideName()+", ");
}
System.out.println("----------该成员所属势力-------------"+sb);
List users = us.getUsersBySideId(100);
StringBuilder sb2 = new StringBuilder();
for (User user : users) {
sb2.append(user.getName() + ", ");
}
System.out.println("----------该势力拥有的成员-------------"+sb2);
输出:
----------该成员所属势力-------------艾米帝国, 哈米尔王国, 精灵王国,
----------该势力拥有的成员-------------艾米, 池寒枫, 大青山,
报错:
1、Cannot find class: userMap 原因:
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/self/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'userMap'. Cause: java.lang.ClassNotFoundException: Cannot find class: userMap
Mybatis与Spring集成
框架搭建的核心就是配置文件。
实例:
pom.xml依赖
junit
junit
4.11
test
org.mybatis
mybatis
3.4.6
mysql
mysql-connector-java
5.1.31
org.junit.jupiter
junit-jupiter-api
RELEASE
compile
com.alibaba
fastjson
1.2.60
org.mybatis
mybatis-spring
1.3.2
org.springframework
spring-context
4.3.8.RELEASE
org.springframework
spring-tx
3.2.7.RELEASE
org.springframework
spring-jdbc
3.1.3.RELEASE
org.springframework
spring-beans
4.3.8.RELEASE
org.springframework
spring-core
4.3.8.RELEASE
org.apache.commons
commons-lang3
3.8.1
org.springframework
spring-web
4.0.9.RELEASE
org.springframework
spring-webmvc
4.0.9.RELEASE
org.springframework
spring-aop
4.3.8.RELEASE
org.aspectj
aspectjweaver
1.9.2
web.xml 配置spring 和springmvc容器
hellomybatis
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
WEB-INF/hellomybatis-servlet.xml
1
hellomybatis
/
contextConfigLocation
classpath*:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
hellomybatis-servlet.xml springmvc配置文件
applicationContext.xml spring配置文件
mybatis-config.xml mybaits配置文件可以不写了
UserMapper.xml
@Repository
//@Mapper
public interface UserMapper {
public List getUsers();
}
public interface UserService {
public List getUsers();
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public List getUsers() {
return userMapper.getUsers();
}
}
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/user")
public void getUsers(){
System.out.println(JSON.toJSONString(userService.getUsers()));
}
}
输出:
[{"dept":"amy empire","height":1.75,"id":1,"name":"艾米","phone":"18956563228"},{"dept":"amy empire","height":1.85,"id":2,"name":"大青山","phone":"18956563229"},{"dept":"amy empire","height":1.83,"id":3,"name":"池寒枫","phone":"22056545"},{"dept":"森林矮人王国","height":1.56,"id":4,"name":"霍恩斯","phone":"852-253521"}]
报错:
1、@RequestMapping("/user") 注解用不了。
org.springframework
spring-web
4.0.9.RELEASE
2、import不了DispatcherServlet
org.springframework
spring-webmvc
4.0.9.RELEASE
3、No bean named 'userController' available,找不到bean。报这个错是因为使用容器扫描配置时注释了过滤的Controller类型,但是没有把use-default-filters="false" 配置删了,倒置没有过滤的Controller配置的话,那么默认是全部过滤,就没有bean实例被扫描进去了。要改成下面这样。
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userController' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
context:component-scan除了具有context:annotation-config的功能之外,context:component-scan还可以在指定的package下扫描以及注册javabean 。还具有自动将带有@component,@service,@Repository等注解的对象注册到spring容器中的功能。
因此当使用 context:component-scan 后,就可以将 context:annotation-config移除。
4、Result Maps collection already contains value for com.self.dao.SideMapper.sideMap .集合容器里已经有该类型的值了。这个报错是因为当配置了mybatis-config.xml文件的mappers的同时还注册了MapperScannerConfigurer 的自动扫描MyBatis的接口并装配。注册了两遍。因此只要配置一个就行了,我这边是把mybatis-config.xml文件注释掉了。
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'file [F:\self\hellomybatis\target\classes\com\self\dao\SideMapper.xml]'. Cause: java.lang.IllegalArgumentException: Result Maps collection already contains value for com.self.dao.SideMapper.sideMap
5、Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService'。注册userControllerbean时依赖的userService不能得到的注入。
这个原因还是出在spring 容器扫描配置配置上,use-default-filters="false" 使用不当。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.self.service.UserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
use-default-filters 属性的默认值为 true,即使用默认的 Filter 进行包扫描,而默认的 Filter 对标有 @Service,@Controller和@Repository 的注解的类进行扫描。设置为 false,即不使用默认的 Filter 进行扫描。也就是不扫描,只扫描下面配置的include-filter,下面配置的扫描注解有几个就只扫几个,有一个就扫一个。
我们希望 SpringMVC容器 只来控制网站的跳转逻辑,所以我们只希望 SpringMVC 的配置扫描 @Controllerce 注解标注的类,不希望它扫描其余注解标注的类,所以设置了 use-default-filters 为 false,并使用 context:include-filter 子标签设置其只扫描带有 @Controller 注解标注的类。
Spring 容器就不同了,我们希望 Spring 只不扫描带有 @Controller 注解标注的类,而扫描其他注解标注的类,而这时建立在使用默认的 Filter 进行扫描的基础上,设置了 context:exclude-filter 标签,不扫描 @Controller 注解标注的类,所以不应该设置 use-default-filters 为 false 。
use-default-filters="false" 需要和 context:include-filter 一起使用,而不能和 context:exclude-filter 属性一起使用。
参考
MyBatis分页
分页可以分为逻辑分页和物理分页。
逻辑分页是我们的程序在显示每页的数据时,首先查询得到表中的1000条数据,然后根据当前页的“页码”选出其中的100条数据来显示。 物理分页是程序先判断出该选出这1000条的第几条到第几条,然后数据库根据程序给出的信息查询出程序需要的100条返回给我们的程序。
分页待学习其他的方法,这个不好。
MyBatis动态SQL语句
mybatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:
- if 语句 (简单的条件判断)
- choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似.
- trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
- where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
- set (主要用于更新时)
- foreach (在实现 mybatis in 语句查询时特别有用)
update t_user
name = #{name},
dept = #{dept},
phone = #{phone},
height = #{height},
modify_emp = #{modifyEmp},
modify_time = #{modifyTime},
where id = #{id}
update t_user
name = #{name},
dept = #{dept},
phone = #{phone},
height = #{height},
modify_emp = #{modifyEmp},
modify_time = #{modifyTime},
where id = #{id}
insert id="insert">
select seq_users.nextval from dual
select nextval for seq_users from sysibm.sysdummy1"
insert into users values (#{id}, #{name})
prefixOverrides使用参考
foreach查询的写法
public class User {
/**
* 成员所属势力群
*/
private List sides;
/**
* id
*/
private int id;
private List ids;
private Integer[] idsArr;
/**
* 名字
*/
private String name;
/**
* 部门,帝国
*/
private String dept;
/**
* 联系号码
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
//private List weibos;
/**
* 创建人
*/
private Long createEmp;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改时间
*/
private Date modifyTime;
public Integer[] getIdsArr() {
return idsArr;
}
public void setIdsArr(Integer[] idsArr) {
this.idsArr = idsArr;
}
public List getIds() {
return ids;
}
public void setIds(List ids) {
this.ids = ids;
}
}
测试类:
User user = new User();
Integer[] idsArr = {1,2,3,4};
user.setIdsArr(idsArr);
user.setDept("amy empire");
uc.getUserCondition(user);
//===============================================
User user = new User();
List ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
user.setIds(ids);
user.setDept("amy empire");
uc.getUserCondition(user);
mybaits if标签语句
实例:
错误:
Exception in thread "main" org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'name != null AND name !='' '. Cause: org.apache.ibatis.ognl.ExpressionSyntaxException: Malformed OGNL expression: name != null AND name !='' [org.apache.ibatis.ognl.ParseException: Encountered " "AND "" at line 1, column 14.
Was expecting one of:
AND phone = #{phone,jdbcType=VARCHAR}
and phone = #{phone,jdbcType=VARCHAR}
!和= 之间多了一个空格也会报错,if test="phone ! = null and phone ! ='' 。应该去掉空格
and phone = #{phone}
MyBatis where标签语句
“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果where标签返回的内容是以 AND 或OR 开头的,则它会剔除掉。
Mybatis set标签
当 update 语句中没有使用 if 标签时,如果有一个参数为 null,都会导致错误。
当在 update 语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。使用 if+set 标签修改后,如果某项为 null 则不进行更新,而是保持数据库原值。
报错:如果 set 包含的内容为空的话则会出错 ,如下。
update t_user where id = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where id = 2' at line 3
;
Mybatis trim标签
trim 是更灵活用来去处多余关键字的标签,它可以用来实现 where 和 set 的效果。trim 元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是 prefix 和 suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是 prefixOverrides 和 suffixOverrides;正因为 trim 有这样的功能,所以我们也可以非常简单的利用 trim 来代替 where 元素的功能。
choose (when, otherwise)标签
有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。而使用if标签时,只要test中的表达式为 true,就会执行 if 标签中的条件。MyBatis 提供了 choose 元素。if标签是与(and)的关系,而 choose 是或(or)的关系。
choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
MyBatis SqlSessionDaoSupport实例
待验证。没用
MyBatis打印输出SQL语句
实例:
log4j.properties
log4j.rootLogger=debug,stdout,logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=C:/mybatis_show_sql.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
pom.xml依赖
org.slf4j
slf4j-api
1.7.2
log4j
log4j
1.2.17
org.slf4j
slf4j-log4j12
1.7.21
输出:
DEBUG - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@647f63b4] was not registered for synchronization because synchronization is not active
DEBUG - Fetching JDBC Connection from DataSource
DEBUG - Creating new JDBC DriverManager Connection to [jdbc:mysql://localhost:3306/hello_mybatis]
DEBUG - JDBC Connection [com.mysql.jdbc.JDBC4Connection@481b3df0] will not be managed by Spring
DEBUG - ==> Preparing: select * from `t_user` WHERE id in ( ? , ? , ? )
DEBUG - ==> Parameters: 1(Integer), 2(Integer), 3(Integer)
DEBUG - <== Total: 3
DEBUG - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@647f63b4]
DEBUG - Returning JDBC Connection to DataSource
ERROR - [{"dept":"amy empire","height":1.75,"id":1,"name":"大青山","phone":"18956563228"},{"dept":"amy empire","height":1.85,"id":2,"name":"艾米哈珀","phone":"18956563228"},{"dept":"amy empire","height":1.83,"id":3,"name":"池寒枫","phone":"22056545"}]
疑问
Q:什么是OGNL表达式?
A:对象导航图语言(Object Graph Navigation Language),简称OGNL,是应用于Java中的一个开源的表达式语言(Expression Language),它被集成在mybatis等框架中,作用是对数据进行访问,它拥有类型转换、访问对象方法、操作集合对象等功能。
Q:像下面这种变量要怎么配置各个环境的变量,方便替换,怎么读取到相应的properties文件?value="${driver}"
Q:MyBatis ofType和javaType区别?
A:JavaType和ofType都是用来指定对象类型的,但是JavaType是用来指定pojo中属性的类型,而ofType指定的是映射到list集合属性中pojo的类型。
Q:下面两个resultMap有什么区别么?为什么要多指导jdbcType 和javaType,去掉这两个后运行程序还是正常,那这两个有什么作用?
Q:@Mapper注解的作用是什么?
@Mapper
public interface UserMapper {}
Q:在写mybatissql时为什么查询绑定的值还要指定上jdbcType类型呢?不写也是可以的,写和不写的区别?如下
and phone = #{phone,jdbcType=VARCHAR}
Q:有空研究下log4j.properties上的具体配置如何操作,自定义,形成文档。
参考
官网文档-中文
yiibai教程