创建一个空Maven项目
依赖在pom.xml
导入
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.4version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
dependency>
dependencies>
在src/main/resource
中创建mybatis-config.xml
文件
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
configuration>
与MySQL5.x不同的是,MySQL8.x的驱动是
com.mysql.cj.jdbc.Driver
url中需要设置是否开启SSL连接:
useSSL=true
/设置编码:useUnicode=true&characterEncoding=utf8
并且,MySQL8.x必须设置时区
serverTimezone=Asia/Shanghai
在这里
&
需要转义成&
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory = null;
//获取SqlSessionFactory对象
static{
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession实例
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
SqlSession中包含了面向数据库执行的SQL命令的所有方法
在pero.fisher.pojo
中创建一个与表相关的类
public class User {
private int id;
private String name;
private String passwd;
public User() {
}
public User(int id, String name, String passwd) {
this.id = id;
this.name = name;
this.passwd = passwd;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getPasswd() {
return passwd;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
在pero.fisher.dao
中
创建UserMapper
接口
public interface UserMapper {
List<User> getUserList();
}
创建UserMapper.xml
用来实现UserMapper接口
<mapper namespace="pero.fisher.dao.UserDao">
<select id="getUserList" resultType="pero.fisher.pojo.User">
select * from mybatis.user
select>
mapper>
在test.java.pero.fisher.dao
中编写测试类UserDaoTest
public class UserDaoTest {
@Test
public void test(){
//获取SqlSession对象
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//执行SQL
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
//关闭SqlSession
sqlSession.close();
}
}
如果你遇到下面这个错误
org.apache.ibatis.binding.BindingException: Type interface pero.fisher.dao.UserDao is not known to the MapperRegistry.
这个错误是绑定异常
每个Mapper.xml文件都需要在MyBatis核心配置文件注册
解决办法
在核心配置文件mybatis-config.xml
中加入
<mappers>
<mapper resource="pero/fisher/dao/UserMapper.xml" />
mappers>
如果你遇到下面这个错误
java.lang.ExceptionInInitializerError
### The error may exist in pero/fisher/dao/UserMapper.xml
这是一个文件过滤问题
原因是Maven默认的配置文件是存放在resource中,当前
Mapper.xml
是放在java
中,此时Maven无法找到这个文件
解决办法
在工程配置文件pom.xml
中添加
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
这样可以打开java下的文件过滤
增删改操作需要提交事务
UserMapper.java
中
//添加一行数据
int addUser(User user);
UserMapper.xml
中
<insert id="addUser" parameterType="pero.fisher.pojo.User" >
insert into mybatis.user (id, name, passwd) values(#{id}, #{name}, #{passwd})
insert>
id: 方法名
parameterType: 参数类型,如果是对象时,需要写权限定名
参数如果是对象,对象中的属性可以直接取出来
UerMapperTest.java
中
@Test
public void assUserTest(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
int ret = userDao.addUser(new User(4, "找流", "678"));
System.out.println(ret);
//提交事务
sqlSession.commit();
sqlSession.close();
}
增删改操作需要
提交事务
SqlSession使用完需要
.close()
关闭
UserMapper.java
中
//删除一行数据
int delUser(int id);
UserMapper.xml
中
<delete id="delUser" parameterType="int">
delete from mybatis.user where id = #{id}
delete>
UserMapper.java
中
//修改数据
int update(User user);
UserMapper.xml
中
<update id="update" parameterType="pero.fisher.pojo.User">
update mybatis.user set name = #{name}, passwd = #{passwd} where id = #{id}
update>
利用Map传参可以让传递参数变得更加灵活,也可以应用Map传递多个参数
UserMapper.java
中
int update2(Map<String, Object> map);
Map的类型为
UserMapper.xml
中
<update id="update2" parameterType="map">
update mybatis.user set name = #{username} where id = #{id}
update>
Map传参不会像传递pojo类一样严格要求#{}中的参数名与类中相同.
也不必在调用方法时构建一个类,特别是当需要的参数很少,而类中的参数很多,构建类的过程会显得十分臃肿和麻烦.
UserMapperTest.java
中
@Test
public void update2Test() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("username", "flash");
map.put("id", 1);
int ret = mapper.update2(map);
System.out.println(ret);
sqlSession.commit();
sqlSession.close();
}
在调用方法时使用put()为Map传值
MyBatis默认事务管理器是JDBC,默认连接池(POOLED)
使用配置文件配置环境
在resources目录中创建db.properties
文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306?\
useSSL=false&useUnicode=TRUE&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=root
password=root
在mybatis-config.xml
中引入配置文件
<configuration>
<properties resource="db.properties" />
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="pero/fisher/dao/UserMapper.xml" />
mappers>
configuration>
为Java类型设置一个短的名字,用来减少完全限定名的冗余
在mybatis-congif.xml
中
可以自定义类型别名
<typeAliases>
<typeAlias type="pero.fisher.pojo.User" alias="User"/>
typeAliases>
也可以指定包名,MyBatis会使用 Bean 的首字母小写的非限定类名来作为它的别名
<typeAliases>
<package name="pero.fisher.pojo"/>
typeAliases>
指定包名也可以自定义别名,当类有@Alias("name")
时,会使用注解值作为别名
@Alias("author")
public class Author {
...
}
告诉 MyBatis 到哪里去找到SQL语句
1. 使用相对于类路径的资源引用
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
mappers>
这是最常用的方法
2. 使用映射器接口实现类的完全限定类名
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
mappers>
这种方法有两个要求
- 接口与它的SQL文件必须同名
- 接口与它的SQL文件必须在同一个包下
3. 将包内的映射器接口实现全部注册为映射器
<mappers>
<package name="org.mybatis.builder"/>
mappers>
这种方法的要求与第二种方法相同
运用ResultType访问数据库,数据库字段名必须与ResultType类中的属性名一一对应
当属性名与字段名不同时可以通过结果集映射,映射字段名对应的方法名
<resultMap id="UserMap" type="user">
<result column="id" property="id"/>
<result column="passwd" property="pass"/>
resultMap>
<select id="getUserById" parameterType="int" resultMap="UserMap">
select * from mybatis.user where id = #{id}
select>
将resultType修改成resultMap,然后定义resultMap
在resultMap中只需要映射不同的部分,属性名与字段名相同的可以自动映射
STDOUT_LOGGING
为默认日志工厂
在mybatis-config.xml
中添加
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
设置日志工厂之后,在调用Mapper方法时会显示出log
log4j的使用
pom.xml
中导入log4j包
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
resources
中编写log4j配置文件log4j.properties
### set log levels ###
# 日志级别为DEBUG输出在Console和File
log4j.rootLogger = DEBUG,Console,File
### 输出到控制台 ###
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.Threshold=DEBUG
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%d{yy/MM/dd HH:mm:ss:SSS}]-%l:%m%n
### 输出到日志文件 ###
log4j.appender.File=org.apache.log4j.RollingFileAppender
log4j.appender.File.File=./log/app.log
log4j.appender.File.MaxFileSize=10MB
log4j.appender.File.Threshold=ALL
log4j.appender.File.layout=org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c]%m%n
# 日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.mysql=DEBUG
log4j.logger.java.mysql.Statement=DEBUG
log4j.logger.java.mysql.ResultSet=DEBUG
log4j.logger.java.mysql.PreparedStatement=DEBUG
mybatis-conifg.xml
中<settings>
<setting name="logImpl" value="log4j"/>
settings>
@Test
public void mytest() {
log.info("普通信息");
log.debug("debug信息");
log.error("错误信息");
}
SQL语句中使用limit分页
select * from user limit startIndex, pageSize;
MyBatis实现分页
UserMapper.java
中
//分页查询
List<User> getUserByLimit(Map<String, Object> map);
UserMapper.xml
中
<resultMap id="UserMap" type="user">
<result column="passwd" property="pass"/>
resultMap>
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex}, #{pageSize}
select>
由于Bean中的属性名与数据库字段名不一致,因此这里应用了
resultMap
进行结果集映射
UserMapperTest.java
中
@Test
public void getUserByLimitTest() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("startIndex", 0);
map.put("pageSize", 2);
List<User> userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
}
SqlSessionFactory.openSession()
方法中存在一个boolean类型参数boolean autoCommit
,默认为false,当传递参数为true时,创建的SqlSession可以自动提交事务.
在Mapper中可以通过注解@Select("")
/@Insert("")
/@Update("")
/@Delete("")
直接写实现,而不需要单独写一个Mapper.xml
//根据id查询数据
@Select("select * from mybatis.user where id = #{uid}")
User getUserById(@Param("uid") int id);
@Param注解可以标注某个参数在sql里面的名字.
在Mapper接口需要传递多个参数时,必须要使用@Param注解
传递一个基本类型的参数时也建议使用@Param注解
上面的实例代码也使用了@Param注解
@Select("select * from mybatis.user where id = #{uid}")
User getUserById(@Param("uid") int id);
假如有一个学生和老师两张表,学生与老师是多对一的关系,即学生中有一个外键指向老师,表结构如下:
需求是要查询出学生的信息,包括学生对应老师的信息
重点是pojo类中需要得到一个类
方式一: 按照查询嵌套处理
在Mapper中写一个接口
//查询所有学生及对应老师的信息
List<Student> getStudent();
Mapper.xml的实现
<select id="getStudent" resultMap="StudentTeacher">
select * from mybatis.student
select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
resultMap>
<select id="getTeacher" resultType="teacher">
select * from mybatis.teacher where id = #{id}
select>
这是一种
子查询
的方法当查询学生时,只能查询到tid,而不是tid对应的Teacher类,因此需要通过
resultMap
将数据库中的tid字段转换成Mapper中的Teacher类
方法二: 按照结果嵌套处理
<select id="getStudent" resultMap="StudentTeacher">
select s.id sid, s.name sname, t.name tname
from mybatis.teacher t, mybatis.student s
where s.tid = t.id
select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="name" column="tname"/>
association>
resultMap>
这是一种连表查询的方法
直接通过sql语句查询所有信息
由于Student有一个参数是Teacher,查询出来的数据需要通过resultMap指定
一对多的需求是在pojo类中需要得到一个集合而并非一个类
一对多与多对一的方法差类似
<select id="getTeacher" resultMap="TeacherStudent">
select t.id tid, t.name tname, s.name sname
from mybatis.teacher t, mybatis.student s
where s.tid = t.id and t.id = #{tid}
select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="student">
<result property="name" column="sname"/>
collection>
resultMap>
有几处不同的是
- 集合需要用
标签而不是
- 集合的类型要用
ofType
而不是javaType
根据不同的条件生成不同的SQL语句
<select id="getBlogsIf" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title != null">
title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
where>
select>
标签可以智能判断如果语句中不需要and会自动将and去掉
相当于switch
语句
<select id="getBlogsChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
when>
<when test="author != null">
and author = #{author}
when>
<when test="views != null">
and views = #{views}
when>
<otherwise>
and views = 1000
otherwise>
choose>
where>
select>
标签相当于switch语句, 当一个条件满足时,choose会终止, 不会执行下一个条件
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title},
if>
<if test="author != null">
author = #{author}
if>
set>
where id = #{id}
update>
标签与标签有相似的功能,就是智能去除SQL拼接时多余的
,
用sql代码片段实现代码服用
<sql id="if-title-author">
<if test="title != null">
title = #{title},
if>
<if test="author != null">
author = #{author}
if>
sql>
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<include refid="if-title-author">include>
set>
where id = #{id}
update>
通过写sql片段,通过引用sql片段
例如select * from mybatis.blog where (id = 1 or id = 2 or id = 3)
这样的语句, 需要获取一串元素, 就可以用
实现
<select id="getBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
foreach>
where>
select>
collection: 遍历的
item: 遍历出来的元素
open: 开始字符
close: 结束字符
separator: 分割字符
查询数据时,频繁连接数据库十分耗资源,
因此引入缓存机制,将查询的结果放入缓存中,当再次查询相同数据时,直接查缓存
一级缓存也叫本地缓存
MyBatis中默认开启一级缓存
一级缓存只在一次SqlSession中有效,即在创建SqlSession到关闭SqlSession之间有效
一级缓存失效的情况
二级缓存也叫全局缓存
步骤
开启全局缓存, 在mybatis-config.xml
配置文件中
<setting name="cacheEnabled" value="true"/>
在要使用二级缓存的Mapper中开启缓存
<cache/>
也可以自定义参数
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
二级缓存的异常处理:
在直接使用
时,会出现异常Cause: java.io.NotSerializableException: pero.fisher.pojo.Blog
这个错误是由二级缓存使用时,是从一级缓存中获取,实体类需要进行序列化
序列化的方法:
Serializable
即可