前言
本文整理一下MyBatis的基本使用,这里非工作中使用的场景,主要为了分析源码而做准备
预备数据库
如果你使用docker的话,推荐看看我的文章把 [Docker 实战系列 - MySQL环境](…/…/Docker/实战系列/Docker 实战系列 - MySQL环境.md)
创建数据库
# 创建库
create database mybatis_demo;
创建表
CREATE TABLE `student` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`cls_id` int(10) NOT NULL COMMENT '班级ID',
`name` varchar(32) NOT NULL COMMENT '名字',
`age` int(3) NOT NULL COMMENT '年龄',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `cls` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '班级名称',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
插入测试数据
INSERT INTO `student`(`id`, `cls_id`, `name`, `age`, `create_time`) VALUES (1, 1, '张三', 13, now());
INSERT INTO `student`(`id`, `cls_id`, `name`, `age`, `create_time`) VALUES (2, 2, '李四', 14, now());
INSERT INTO `cls`(`id`, `name`, `create_time`) VALUES (1, '初一1班', now());
INSERT INTO `cls`(`id`, `name`, `create_time`) VALUES (2, '初一2班', now());
创建项目
在上文中已经创建好了mybatis的源码环境,现在我们开始创建自己的项目
设置自己的包名,我为了以后复习所以用了 chapter1
准备工作
配置properties文件
vim application.properties
jdbc.driver=com.mysql.jdbc.Driver
default.environment=dev
全局配置
vim mybatis-config.xml
属性配置优先级
- build方式
- 外部的 properties 文件
- xml配置的properties
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 这里只是演示,正常你使用 app.properties 配置即可 -->
<properties resource="application.properties">
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
</properties>
<!-- 如果需要远程加载的话可以使用url 只能选一种-->
<!-- <properties url=""> -->
<environments default="${default.environment}">
<!-- 测试环境 -->
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="jdbc:mysql://localhost/mybatis_demo"/>
<property name="username" value="root"/>
<property name="password" value="a123456"/>
</dataSource>
</environment>
<!-- 生产环境 -->
<environment id="pro">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="jdbc:mysql://192.168.160.130/mybatis_online"/>
<property name="username" value="root"/>
<property name="password" value="Aa123456"/>
</dataSource>
</environment>
</environments>
<mappers>
...
</mappers>
</configuration>
定义 Student 并使用 lombok [Lombok 的使用](…/…/Java/编程技巧/Lombok 的使用.md)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer id;
private Integer clsId;
private String name;
private Date createTime;
}
现在项目结构
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── xm
│ │ └── chapter1
│ │ ├── MainTest.java
│ │ ├── Student.java
│ │ └── StudentMapper.xml
│ └── resources
│ ├── application.properties
│ └── mybatis-config.xml
└── test
└── java
XML方式
自定义Mapper.xml
vim StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xm.chapter1.StudentMapper">
<select id="selectStudentXml" resultType="com.xm.chapter1.Student" >
select * from student where id = #{id}
</select>
</mapper>
修改全局配置的mapper,并引入我们的文件
vim mybatis-config.xml
<configuration>
...
<mappers>
<mapper resource="com/xm/chapter1/StudentMapper.xml"/>
</mappers>
</configuration>
运行
public class MainTest {
private SqlSession session;
@Before
public void init() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession(true);
}
@Test
public void test1() throws IOException {
// 通过xml查询
Student result = session.selectOne(
"com.xm.chapter1.StudentMapper.selectStudentXml", 1);
System.out.println(result.toString());
}
}
对于结果的转移,不知道你有没有发现两个字段(createTime,clsId)为空,我们来修改一下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xm.chapter1.StudentMapper">
<resultMap id="StudentMap" type="com.xm.chapter1.Student">
<id property="id" column="id"/>
<result property="clsId" column="cls_id"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectStudentXml" resultMap="StudentMap" >
select * from student where id = #{id}
</select>
</mapper>
接口方式
为了避免混淆,可以先把上面的xml删除,或者不删除屏蔽掉下面的即可
修改全局配置,引入接口方式
<configuration>
...
<mappers>
<mapper class="com.xm.chapter1.StudentMapper"/>
</mappers>
</configuration>
创建mapper接口
public interface StudentMapper {
@Select("select * from student where id = #{id}")
Student selectInterface(Integer id);
}
运行
public class MainTest {
....
@Test
public void test2(){
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student student = mapper.selectInterface(1);
System.out.println(student);
}
}
对于结果的转移,不知道你有没有发现两个字段(createTime,clsId)为空,我们来修改一下
public interface StudentMapper {
@Select("select id,name,cls_id as clsId,create_time as createTime from student where id = #{id}")
Student selectInterface(Integer id);
}
类型处理器
持久层框架其中比较重要的工作就是处理数据的映射转换,把java 类型转换成jdbc 类型的参数,又需要把jdbc 类型的结果集转换成java 类型。
public interface TypeHandler<T> {
void setParameter(
PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
我们将上面的student的事件类型,修改为long试下
...
public class Student {
private Integer id;
private String name;
private Integer clsId;
// 改这里
private Long createTime;
}
运行后发现结果不符合我们预期
Student(id=1, name=张三, clsId=1, createTime=2019)
自定义TypeHandler解决这个问题
public class LongTimeHandler extends BaseTypeHandler<Long> {
@Override
public void setNonNullParameter(
PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
ps.setDate(i, new Date(parameter));
}
@Override
public Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getDate(columnName).getTime();
}
@Override
public Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getDate(columnIndex).getTime();
}
@Override
public Long getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getDate(columnIndex).getTime();
}
}
全局配置的方式 - 任选一
<configuration>
<typeHandlers>
<typeHandler handler="com.xm.chapter1.LongTimeHandler"
javaType="long" jdbcType="TIMESTAMP" />
</typeHandlers>
</configuration>
// 如果嫌弃上面写 javaType、jdbcType 麻烦,可以使用注解
@MappedJdbcTypes(JdbcType.TIMESTAMP)
@MappedTypes(Long.class)
public class LongTimeHandler extends BaseTypeHandler<Long> {
....
}
针对单个返回值 - 任选一
<mapper namespace="com.xm.chapter1.StudentMapper">
<resultMap id="StudentMap" type="com.xm.chapter1.Student">
...
<result property="createTime"
column="create_time" typeHandler="com.xm.chapter1.LongTimeHandler"/>
</resultMap>
....
</mapper>
返回结果
Student(id=1, name=张三, clsId=1, createTime=1562342400000)
日志
创建文件
vim resources/log4j.properties
log4j.properties
### 设置日志级别 ###
log4j.rootLogger = debug,stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c:%L - %m%n