创建项目
- 创建Maven项目
- 添加Mybatis依赖
org.mybatis
mybatis
3.5.3
- 添加Log4j、JUnit和Mysql依赖
mysql
mysql-connector-java
5.1.46
junit
junit
4.12
org.slf4j
slf4j-api
1.7.26
org.slf4j
slf4j-log4j12
1.7.26
log4j
log4j
1.2.17
配置Mybatis
创建配置文件
在src/main/resources下创建mabatis-config.xml
创建数据库表、实体类和映射文件
- 数据库表
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` varchar(50) DEFAULT NULL COMMENT '用户名',
`user_password` varchar(50) DEFAULT NULL COMMENT '密码',
`user_email` varchar(50) DEFAULT '[email protected]' COMMENT '邮箱',
`user_info` text DEFAULT NULL COMMENT '简介',
`head_img` blob DEFAULT NULL COMMENT '头像',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表' |
CREATE TABLE `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(50) DEFAULT NULL COMMENT '角色名',
`enabled` int(11) DEFAULT NULL COMMENT '有效标志',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表' |
CREATE TABLE `sys_user_role` (
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`role_id` bigint(20) DEFAULT NULL COMMENT '角色ID'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色关联表'
- 实体类
/**
* 用户表
*/
public class SysUser implements Serializable {
/**
* 用户ID
* 不要使用基本数据类型
* 原因: 如果使用long id, 那么id默认为0, 如果在动态语句中使用 id != null进行判断, 结果总会为true
*/
private Long id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String userPassword;
/**
* 邮箱
*/
private String userEmail;
/**
* 简介
*/
private String userInfo;
/**
* 头像
* byte[] 这个类型一般对应数据库中的BLOB、LONGVARBINARY以及一些二进制流有关的字段类型
*/
private byte[] headImg;
/**
* 创建时间
*/
private Date createTime;
省略getter、setter方法
}
/**
* 角色表
*/
public class SysRole implements Serializable {
/**
* 角色ID
*/
private Long id;
/**
* 角色名
*/
private String roleName;
/**
* 有效标志
*/
private Integer enabled;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private Date createTime;
省略getter、setter方法
}
- 映射文件
/resources/com/_54programer/xml/mapper/UserMapper.xml
/resources/com/_54programer/xml/mapper/RoleMapper.xml
- 配置Log4j
/resources/log4j.properties
# 全局配置
log4j.rootLogger=ERROR, stdout
# mybatis日志配置
# mybatis日志最低级别是TRACE,在这个日志级别下,会输出sql执行的详细信息,特别适合在开发时使用
log4j.logger.com._54programer.xml.mapper=TRACE
# 控制台输出配置
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
到这个我们的Mybatis环境就配置好了
使用Mybatis进行增删该查
Select用法
- 根据ID查询用户信息
package com._54programer.xml.mapper;
import com._54programer.xml.domain.SysUser;
public interface UserMapper {
SysUser selectById(Long id);
}
package com._54programer.xml.mapper;
import com._54programer.xml.domain.SysUser;
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 org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
public class UserMapperTest {
private static SqlSessionFactory sqlSessionFactory;
@BeforeClass
public static void init(){
try {
//1.通过Resources工具类将mybatis-config.xml读入Reader
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//2.创建SqlSessionFactory, 在创建SqlSessionFactory的过程中
//首先解析mybatis-config.xml配置文件, 然后根据mappers配置读取全部的Mapper.xml进行具体的解析
//解析完成后, 创建SqlSessionFactory就具有所有的属性配置和执行sql的信息
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void TestSelectById(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SysUser sysUser = mapper.selectById(1L);
sqlSession.close();
}
}
运行输出:
DEBUG [main] - ==> Preparing: select * from sys_user where id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <== Row: 1, admin, 123456, [email protected], <>, <>, 2016-06-07 01:11:12.0
DEBUG [main] - <== Total: 1
- 查询全部用户信息
public interface UserMapper {
List selectAll();
}
@Test
public void TestSelectAll(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List sysUsers = mapper.selectAll();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: select id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, create_time createTime from sys_user
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: id, userName, userPassword, userEmail, userInfo, headImg, createTime
TRACE [main] - <== Row: 1, admin, 123456, [email protected], <>, <>, 2016-06-07 01:11:12.0
TRACE [main] - <== Row: 1001, test, 123456, [email protected], <>, <>, 2016-06-07 00:00:00.0
DEBUG [main] - <== Total: 2
Mybatis可以自动将以下画线方式命名的数据库列映射到Java对象的驼峰式命名属性中。所以我们还可以这样写
@Test
public void TestSelectAll(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List sysUsers = mapper.selectAll();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: select id, user_name, user_password, user_email, user_info, head_img, create_time from sys_user
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <== Row: 1, admin, 123456, [email protected], <>, <>, 2016-06-07 01:11:12.0
TRACE [main] - <== Row: 1001, test, 123456, [email protected], <>, <>, 2016-06-07 00:00:00.0
DEBUG [main] - <== Total: 2
- 多表关联查询
方式1: 重新定义一个继承SysUser的扩展类SysUserExtend
public class SysUserExtend extends SysUser {
private String roleName;
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
public interface UserMapper {
List selectUserRole(Long id);
}
@Test
public void TestselectUserRole(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List sysUserExtends = mapper.selectUserRole(1L);
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: SELECT u.id, u.user_email, u.user_info, u.user_name, u.user_password, u.head_img, u.create_time, r.role_name from sys_user u LEFT JOIN sys_user_role ur on u.id = ur.user_id LEFT JOIN sys_role r on r.id = ur.role_id WHERE u.id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, user_email, user_info, user_name, user_password, head_img, create_time, role_name
TRACE [main] - <== Row: 1, [email protected], <>, admin, 123456, <>, 2016-06-07 01:11:12.0, 管理员
TRACE [main] - <== Row: 1, [email protected], <>, admin, 123456, <>, 2016-06-07 01:11:12.0, 普通用户
DEBUG [main] - <== Total: 2
方式2: 在SysUser中增加SysRole对象
public class SysUser implements Serializable {
private Long id;
private String userName;
private String userPassword;
private String userEmail;
private String userInfo;
private byte[] headImg;
private Date createTime;
//在SysUser中增加SysRole对象
private SysRole role;
public interface UserMapper {
List selectUserRole(Long id);
}
@Test
public void TestselectUserRole(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.selectUserRole(1L);
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: SELECT u.id, u.user_email, u.user_info, u.user_name, u.user_password, u.head_img, u.create_time, r.role_name as "role.roleName" from sys_user u LEFT JOIN sys_user_role ur on u.id = ur.user_id LEFT JOIN sys_role r on r.id = ur.role_id WHERE u.id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, user_email, user_info, user_name, user_password, head_img, create_time, role.roleName
TRACE [main] - <== Row: 1, [email protected], <>, admin, 123456, <>, 2016-06-07 01:11:12.0, 管理员
TRACE [main] - <== Row: 1, [email protected], <>, admin, 123456, <>, 2016-06-07 01:11:12.0, 普通用户
DEBUG [main] - <== Total: 2
Insert
- 插入一条数据
public interface UserMapper {
int addUser(SysUser sysUser);
}
insert into sys_user(
id, user_name, user_password, user_email, user_info, head_img, create_time)
values(
#{id}, #{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP}
)
@Test
public void TestAddUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SysUser sysUser = new SysUser();
sysUser.setCreateTime(new Date());
sysUser.setUserEmail("[email protected]");
sysUser.setUserInfo("test1...");
sysUser.setUserName("sn");
sysUser.setUserPassword("123456");
mapper.addUser(sysUser);
sqlSession.commit();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: insert into sys_user( id, user_name, user_password, user_email, user_info, head_img, create_time) values( ?, ?, ?, ?, ?, ?, ? )
DEBUG [main] - ==> Parameters: null, sn(String), 123456(String), [email protected](String), test1...(String), null, 2019-11-19 13:41:03.726(Timestamp)
DEBUG [main] - <== Updates: 1
- 使用JDBC方式返回主键ID
这种回写主键的方法只适用于支持主键自增的数据库。有些数据库(如 Oracle )不提供主键自增的功能。
public interface UserMapper {
int addUserGetId(SysUser sysUser);
}
insert into sys_user(
id, user_name, user_password, user_email, user_info, head_img, create_time)
values(
#{id}, #{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP}
)
@Test
public void TestaddUserGetId(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SysUser sysUser = new SysUser();
sysUser.setCreateTime(new Date());
sysUser.setUserEmail("[email protected]");
sysUser.setUserInfo("test1...");
sysUser.setUserName("sn");
sysUser.setUserPassword("123456");
mapper.addUserGetId(sysUser);
//输出新增记录的ID
System.out.println("主键ID: "+sysUser.getId());
sqlSession.commit();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: insert into sys_user( id, user_name, user_password, user_email, user_info, head_img, create_time) values( ?, ?, ?, ?, ?, ?, ? )
DEBUG [main] - ==> Parameters: null, sn(String), 123456(String), [email protected](String), test1...(String), null, 2019-11-19 13:50:06.426(Timestamp)
DEBUG [main] - <== Updates: 1
主键ID: 1037
- 使用selectKey返回主键ID
这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。
insert into sys_user(
id, user_name, user_password, user_email, user_info, head_img, create_time)
values(
#{id}, #{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP}
)
select last_insert_id()
@Test
public void TestaddUserGetId(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SysUser sysUser = new SysUser();
sysUser.setCreateTime(new Date());
sysUser.setUserEmail("[email protected]");
sysUser.setUserInfo("test1...");
sysUser.setUserName("sn");
sysUser.setUserPassword("123456");
mapper.addUserGetId(sysUser);
//输出新增记录的ID
System.out.println(sysUser.getId());
sqlSession.commit();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: insert into sys_user( id, user_name, user_password, user_email, user_info, head_img, create_time) values( ?, ?, ?, ?, ?, ?, ? )
DEBUG [main] - ==> Parameters: null, sn(String), 123456(String), [email protected](String), test1...(String), null, 2019-11-19 14:00:07.414(Timestamp)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> Preparing: select last_insert_id()
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: last_insert_id()
TRACE [main] - <== Row: 1038
DEBUG [main] - <== Total: 1
1038
Update
public interface UserMapper {
int updateUserById(SysUser sysUser);
}
update sys_user
set user_name = #{userName},
user_password = #{userPassword},
user_email = #{userEmail},
user_info = #{userInfo},
head_img = #{headImg, jdbcType=BLOB},
create_time = #{createTime, jdbcType=TIMESTAMP}
where id = #{id}
@Test
public void TestUpdateUserById(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SysUser sysUser = new SysUser();
sysUser.setId(1037L);
sysUser.setCreateTime(new Date());
sysUser.setUserEmail("[email protected]");
sysUser.setUserInfo("test2...");
sysUser.setUserName("sn");
sysUser.setUserPassword("123456789");
int result = mapper.updateUserById(sysUser);
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: update sys_user set user_name = ?, user_password = ?, user_email = ?, user_info = ?, head_img = ?, create_time = ? where id = ?
DEBUG [main] - ==> Parameters: sn(String), 123456789(String), [email protected](String), test2...(String), null, 2019-11-19 14:13:50.226(Timestamp), 1037(Long)
DEBUG [main] - <== Updates: 1
1
Delete
public interface UserMapper {
int deleteUserById(Long id);
}
delete from sys_user where id = #{id}
@Test
public void TestDeleteUserById(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.deleteUserById(1037L);
System.out.println(result);
sqlSession.commit();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: delete from sys_user where id = ?
DEBUG [main] - ==> Parameters: 1037(Long)
DEBUG [main] - <== Updates: 1
1
多参数
我们尝试下传多个参数进行数据查询
public interface UserMapper {
SysUser findUserByIdAndUsername(Long id, String user_name);
}
@Test
public void TestFindByIdAndUsername(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SysUser sysUser = mapper.findUserByIdAndUsername(1L, "admin");
sqlSession.close();
}
运行:
报错了!!!
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.binding.BindingException:
Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
怎么解决? 在参数钱添加@Param注解即可。
public interface UserMapper {
SysUser findUserByIdAndUsername(@Param("id") Long id, @Param("user_name") String user_name);
}
再测试,这次成功了
DEBUG [main] - ==> Preparing: select id, user_name, user_password, user_email, user_info, head_img, create_time from sys_user where id = ? and user_name = ?
DEBUG [main] - ==> Parameters: 1(Long), admin(String)
TRACE [main] - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <== Row: 1, admin, 123456, [email protected], <>, <>, 2016-06-07 01:11:12.0
DEBUG [main] - <== Total: 1
给参数配置@Param注解后,Mybatis就会自动将参数封装成Map类型,@param会作为Map中的key,因此sql就可以通过配置的注解值来使用参数。
Mybatis动态SQL
if
- 查询条件
- 更新字段
update sys_user
set
user_name = #{userName},
user_password = #{userPassword},
user_email = #{userEmail},
user_info = #{userInfo},
head_img = #{headImg, jdbcType=BLOB},
create_time = #{createTime, jdbcType=TIMESTAMP},
id = #{id}
where id = #{id}
choose
where
foreach
- in查询
- 批量插入
insert into sys_user(
id, user_name, user_password, user_email, user_info, head_img, create_time)
values(
#{user.id}, #{user.userName}, #{user.userPassword}, #{user.userEmail},
#{user.userInfo}, #{user.headImg, jdbcType=BLOB}, #{user.createTime, jdbcType=TIMESTAMP}
)
- 动态更新
update sys_user
set
${key} = #{val}
where id = #{id}
Mapper接口动态代理实现原理
我们可能会有一个疑问,为什么Mapper接口没有实现类却能被正常使用?
这是因为Mybatis在Mapper接口上使用了动态代理。
这是我们刚才定义的一个Mapper接口:
public interface UserMapper {
List selectAll();
}
我们使用Java动态代理方式创建一个代理类
public class MyMapperProxy implements InvocationHandler {
private Class mapperInterface;
private SqlSession sqlSession;
public MyMapperProxy(Class mapperInterface, SqlSession sqlSession) {
this.mapperInterface = mapperInterface;
this.sqlSession = sqlSession;
}
/**
* 这里只是简单实现
* 不考虑接口方法中的参数和方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
List list = sqlSession.selectList(mapperInterface.getCanonicalName() + "." + method.getName());
return list;
}
}
@Test
public void Test1(){
SqlSession sqlSession = sqlSessionFactory.openSession();
MyMapperProxy userMapperProxy = new MyMapperProxy(UserMapper.class, sqlSession);
UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{UserMapper.class}, userMapperProxy);
userMapper.selectAll();
sqlSession.close();
}
输出:
DEBUG [main] - ==> Preparing: select id, user_name, user_password, user_email, user_info, head_img, create_time from sys_user
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time
TRACE [main] - <== Row: 1, admin, 123456, [email protected], <>, <>, 2016-06-07 01:11:12.0
TRACE [main] - <== Row: 1001, test, 123456, [email protected], <>, <>, 2016-06-07 00:00:00.0
TRACE [main] - <== Row: 1036, sn, 123456, [email protected], <>, <>, 2019-11-19 13:41:03.0
TRACE [main] - <== Row: 1038, sn, 123456, [email protected], <>, <>, 2019-11-19 14:00:07.0
DEBUG [main] - <== Total: 4
从代理类中可以看到,当调用一个接口的方法时,会先通过接口的全限定名称和当前调用的方法名的组合得到这个方法id ,这个 id 的值就是映射 XML中namespace 和具体方法 id的组合。所以可以在代理方法中使用 sqlSession 以命名空间的方式调用方法。通过这种方式可以将接口和 XML 文件中的方法关联起来。这种代理方式和常规代理的不同之处在于,这里没有对某个具体类进行代理,而是通过代理转化成了对其他代码的调用。