环境
jdk1.8
Mysql 5.7
maven 3.5.4
IDEA
回顾
MyBatis 是一款优秀的持久层框架
它支持自定义 SQL、存储过程以及高级映射
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis
2013年11月迁移到Github。
搭建环境-->导入Mybatis-->编写代码-->测试
在数据库创建一个demo/user表
创建一个普通的maven项目
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
<parent>
<artifactId>mybatis-study</artifactId>
<groupId>com.jmu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
? 2. 父模块中存在
<modules>
<module>mybatis-01</module>
</modules>
mybatis-config.xml
&
表示&<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
编写mybatis的工具类
SqlSessionFactoryBuilder | 一旦创建了 SqlSessionFactory,就不再需要它了 | 局部变量 |
---|---|---|
SqlSessionFactory | SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在 | 应用作用域,比如:使用单例模式或者静态单例模式。 |
SqlSession | 每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它(sqlSession.close()) | 请求或方法作用域,用完关闭 |
SqlSession openSession(boolean var1);
设置成true,就可以实现自动提交事务了,就不需要在增删改中手动写commit
package com.jmu.utils;
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.InputStream;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
//从 XML 中构建 SqlSessionFactory
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//从 SqlSessionFactory 中获取 SqlSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
实体类User
DAO接口
package com.jmu.dao;
import com.jmu.pojo.User;
import java.util.List;
public interface UserDao {
List<User> getUserList();
User getUserByUsername(String username);
}
由原来的Impl文件转化为mapper.xml文件
渐渐的将××DAO.java改成××Mapper.java
<?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.jmu.dao.UserDao">
<select id="getUserByUsername" resultType="com.jmu.pojo.User">
select * from user where username like #{username}
</select>
<select id="getUserList" resultType="com.jmu.pojo.User">
select * from user
</select>
</mapper>
在test/java/com/jmu/dao/UserTest
,在这个地方是为了规范
package com.jmu.dao;
import com.jmu.pojo.User;
import com.jmu.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserTest {
@Test
public void getUserList(){
//第一步 获得sqlSession对象
SqlSession sqlSession = MybatisUtil.getSqlSession();
//第二步 执行SQL getMapper
UserDao userMapper=sqlSession.getMapper(UserDao.class);
System.out.println(userMapper.getUserByUsername("admin"));
List<User> all = userMapper.getUserList();
for(User user:all){
System.out.println(user);
}
//关闭sqlSession
sqlSession.close();
}
}
User{auto_id=1, username=‘admin‘, password=‘123456‘, age=20}
User{auto_id=1, username=‘admin‘, password=‘123456‘, age=20}
User{auto_id=2, username=‘user01‘, password=‘123456‘, age=18}
User{auto_id=3, username=‘user02‘, password=‘密码‘, age=19}
Type interface com.jmu.dao.UserDao is not known to the MapperRegistry.
每一个***Mapper.xml
都需要到mybatis-config.xml
中注册
<mappers>
<mapper resource="com/jmu/dao/UserMapper.xml"></mapper>
</mappers>
Could not find resource com/jmu/dao/UserMapper.xml
Maven的资源过滤问题
需要在pom.xml加入以下代码(在父项目添加即可,如果还不行的话,就在子module的pom.xml也添加)
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
1 字节的 UTF-8 序列的字节 1 无效。
把xml的encoding属性值UTF-8改为UTF8,Mapper.xml文件和mybatis-config.xml都需要改动
namespace中的包名和Dao/Mapper接口的包名一致
增删改需要提交事务
sqlSession.commit
%张%
,List<User> userList = mapper.getUserLike("%李%");
like "%"#{value}"%"
select * from user where name like "%"#{value}"%"
注意点: sqlSession.commit();
<insert id="insertUser" parameterType="com.jmu.pojo.User" >
insert into user(username, password, age) values(#{username},#{password},#{age})
</insert>
@Test
public void insert(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao userMapper = sqlSession.getMapper(UserDao.class);
int n=userMapper.insertUser(new User("admin","123",12));
System.out.println(n);
sqlSession.commit();
sqlSession.close();
}
和insert基本一样
假设,我们的实体类,或者数据库中的表,字段或者参数过多。或者联表查询结果
<insert id="insertUser2" parameterType="map" >
insert into user(username, password, age) values(#{name},#{pwd},#{age})
</insert>
@Test
public void insert(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserDao userMapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("name","zhangsan");
map.put("pwd","mima");
map.put("age",18);
int n=userMapper.insertUser2(map);
System.out.println(n);
sqlSession.commit();
sqlSession.close();
}
<select id="getUserInfo" resultType="map" parameterType="int">
select * from user,address where user.auto_id=address.user_id and address.user_id=#{user_id}
</select>
@Test
public void get(){
SqlSession sqlSession=MybatisUtil.getSqlSession();
UserDao userMapper = sqlSession.getMapper(UserDao.class);
Map<String,Object> map=userMapper.getUserInfo(1);
//遍历key value
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
}
传递类型 | 说明 | 例子 |
---|---|---|
Map传递参数 | 直接在 sql中取出Key即可 | parameter="map" |
对象传递参数 | 直接在sql中取出对象的属性即可 | parameter="com.jmu.pojo.××" |
只有一个基本类型参数 | 可以直接在sql中取到 | parameter="int“ 也可以不写 |
多个参数传递 | 使用Map或者注解 |
configuration(配置)
MyBatis 可以配置成适应多种环境,不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
Mybatis有2种事务管理器(JDBC|MANAGED)默认的事务管理器是JDBC
默认数据源类型是POOLED(池)
我们可以通过properties属性来引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
编写好db.properties
注意这里的&不使用&
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
报错:mybatis核心配置文件,需要注意标签配置顺序
The content of element type "configuration" must match "(properties?,settin....
<properties resource="db.properties" />
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
<typeAliases>
<typeAlias type="com.jmu.pojo.User" alias="User" />
</typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.jmu.pojo"/>
</typeAliases>
每一个在包 com.jmu.pojo
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.jmu.pojo.User
的别名为 user
;
@Alias("author")
public class Author {
...
}
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
方式一【推荐使用】:使用相对于类路径的资源引用
<mappers>
<mapper resource="com/jmu/dao/UserMapper.xml"></mapper>
</mappers>
方式二:使用class文件绑定注册
<mappers>
<mapper class="com.jmu.dao.UserMapper" />
</mappers>
解决方法:
select password as pwd ,username as name,age from user where auto_id=#{auto_id}
<resultMap id="UserMap" type="User">
<result column="username" property="name"/>
<result column="password" property="pwd"/>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from user
</select>
如果一个数据库操作,出现异常,我们需要排错。日志是最好的助手!
曾经:sout debug
现在:日志工厂
logImpl 【掌握】LOG4J |STDOUT_LOGGING(标准日志输出)
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,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=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/jmu.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
import org.apache.log4j.Logger;
static Logger logger = Logger.getLogger(UserTest.class);
logger.info("info:进入testLog4j方法");
logger.debug("debug:进入testLog4j方法");
logger.error("error:进入testLog4j方法");
思考:为什么要分页?
select * from user limit startIndex,pageSize;
使用Mybatis实现分页,核心SQL
//分页
List<User> getUserByLimit(Map<String,Integer> map);
<select id="getUserByLimit" parameterType="map" resultType="User">
select * from demo.user limit #{startIndex},#{pageSize}
</select>
@Test
public void getUserByLimit(){
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("startIndex",1);
map.put("pageSize",2);
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> all = userMapper.getUserByLimit(map);
for (User user : all) {
System.out.println(user);
}
}
我们之前学过面向对象编程,但是真正的开发中,很多时候,我们会选择面向接口编程
根本原因:解耦
映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。比如
package org.mybatis.example;
import org.apache.ibatis.annotations.Select;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
@Select("select * from user where auto_id = #{auto_id}")
User getUserById(@Param("auto_id") int id);
<mappers>
<mapper class="com.jmu.dao.UserMapper" />
</mappers>
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO teacher(`id`, `name`) VALUES (1, ‘秦老师‘);
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (‘1‘, ‘小明‘, ‘1‘);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (‘2‘, ‘小红‘, ‘1‘);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (‘3‘, ‘小张‘, ‘1‘);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (‘4‘, ‘小李‘, ‘1‘);
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (‘5‘, ‘小王‘, ‘1‘);
多个学生对应一个老师
private int id;
private String name;
//学生需要关联一个老师
private Teacher teacher;
可以在resources中新建com.jmu.dao中放Student.xml和TeacherMapper.xml
这样处理,在最后生成的时候会变成
<mappers>
<mapper resource="com/jmu/dao/StudentMapper.xml" />
<mapper resource="com/jmu/dao/TeacherMapper.xml" />
</mappers>
5.测试查询是否成功
<mapper namespace="com.jmu.dao.StudentMapper">
<resultMap id="StudentTeacher" type="Student">
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<association column="tid" property="teacher" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{tid}
</select>
</mapper>
<resultMap id="StudentTeacher2" type="Student">
<result column="sid" property="id"></result>
<result column="sname" property="name"></result>
<association property="teacher" javaType="Teacher" >
<result column="tname" property="name" />
</association>
</resultMap>
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,
s.name sname,
t.name tname
from student s,teacher t
where s.tid=t.id;
</select>
总结:Mysql多对一
一个老师拥有多个学生
public class Student {
private int id;
private String name;
private int tid;
....
}
public class Teacher {
private int id;
private String name;
//一个老师教多个学生
private List<Student> students;
....
}
<resultMap id="TeacherStudent" type="Teacher">
<result column="tid" property="id" />
<result column="tname" property="name" />
<collection property="students" ofType="Student">
<result column="sid" property="id"></result>
<result column="sname" property="name"></result>
</collection>
</resultMap>
<select id="getTeacher" resultMap="TeacherStudent">
select t.id tid,
t.name tname,
s.id sid,
s.name sname
from student s,teacher t
where s.tid=t.id;
</select>
什么是动态SQL:动态SQL就是根据不通过的条件生成不同的SQL语句
if
choose
trim
foreach
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT ‘博客id‘,
`title` VARCHAR(100) NOT NULL COMMENT ‘博客标题‘,
`author` VARCHAR(30) NOT NULL COMMENT ‘博客作者‘,
`create_time` DATETIME NOT NULL COMMENT ‘创建时间‘,
`views` INT(30) NOT NULL COMMENT ‘浏览量‘
)ENGINE=INNODB DEFAULT CHARSET=utf8
public class Blog {
private int id;
private String title;
private String author;
private Date create_time;
private int views;
...
}
test中的title
和"%"#{title}"%"
是后端传过来的map的String,不是数据库的。即
map.put("title1","标题");
<when test="title1 != null">
title like "%"#{title1}"%"
</when>
<select id="getBlogIf" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="title != null">
and title like "%"#{title}"%"
</if>
<if test="views != null">
and views = #{views}
</if>
</select>
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。switch-case
<select id="getBlogIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title != null">
title like "%"#{title}"%"
</when>
<when test="author != null and views != null">
author like #{author} and views = #{views}
</when>
<otherwise>
views >100
</otherwise>
</choose>
</where>
</select>
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
select * from blog
<where>
<if test="title != null">
and title like "%"#{title}"%"
</if>
<if test="views != null">
and views = #{views}
</if>
</where>
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句
的时候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
Mybatis系统默认定义两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存
为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存
又称本地缓存:SqlSession
测试步骤
@Test
public void test(){
SqlSession sqlSession = MybatisUtils getsqlSession();
UserMapper mapper =sqlSession getMapper(UserMapper class);
User user = mapper.queryUserById(1);
System.out.println(user):
System.out.println("======================");
User user2 = mapper.queryUserById(1);
System.out.println(user2):
System.out.println("======================");
System. out. println(user==user2);//返回true,说明两个对象地址引用是相同的
sqlSession.close();
}
//观察日志发现只是走了一次sql
要启用全局的二级缓存,只需要在你的 SQL 映射文件xml中添加一行<cache/>
:
需要开启全局缓存(在setting中)
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
<mapper namespace="com.jmu.dao.BlogMapper">
<cache/>
......
测试
开启两个sqlSession,一个执行完之后,将sqlSession关闭,另一个再去查询,看到SQL执行一次
小结:
除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
原文:https://www.cnblogs.com/10134dz/p/13550541.html