简介
mybatis是一个持久层框架,支持定制化的sql,存储过程以及高级映射,避免了所有jdbc代码和手动参数集以及获取结果集,mybatis可以使用简单的xml或者注解来配置和映射原生类型,接口和java中的pojo
持久层
数据持久化,就是将程序的数据在持久状态和瞬时状态转化的过程。
内存:数据断电就丢失了
数据库(jdbc),io文件持久化
第一个mybatis程序
环境搭建
在navicat中创建数据库
CREATE DATABASE `mybatismmz`;
创建表
CREATE TABLE user(
id INT(20) not null PRIMARY KEY,
name VARCHAR(30) DEFAULT NULL,
pwd VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
别忘符号的关系
插入数据
INSERT INTO user(id,name,pwd) VALUES
(1,'mmz','123'),
(2,'mmz1','213'),
(3,'mmz2','321')
创建一个普通的maven项目,是父工程。导入依赖创建子工程
导入父工程依赖pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mmz</groupId>
<artifactId>MmzMybatisLearning</artifactId>
<version>1.0-SNAPSHOT</version>
<!--导入依赖-->
<dependencies>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
</dependencies>
</project>
创建一个子module
在子module中配置mybatis的
配置文件mybatis-config.xml
<?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>
<!-- 配置环境-->
<environments default="mysql">
<environment id="mysql">
<!-- 事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置数据库的四个基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatismmz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"></property>
<property name="username" value="root"></property>
<property name="password" value="password"></property>
</dataSource>
</environment>
</environments>
<!--如果是用注解来配置的话,应该使用class属性指定被注解的dao全限定类名-->
<mappers>
</mappers>
</configuration>
编写mybatis工具类
每个基于mybatis的应用都是以一个sqlsessionfactory的实例为核心的,sqlsessionfactory的实例可以通过sqlsessionfactorybuilder获得。这个builder可以通过xml配置文件或者一个预定先定制的Configuration的实例构建出来。
可以从sqlsessionfactory中获取sqlsession,因为sqlsession里面包含了所有数据库执行sql命令所需要的方法,你可以通过sqlsession实例来直接执行已经映射的sql语句
编写实体类
package com.mmz.pojo;
/**
* @Classname User
* @Description TODO
* @Date 2020/12/24 21:57
* @Created by mmz
*/
// 实体类
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
编写测试类
package com.mmz.dao;
import com.mmz.pojo.User;
import com.mmz.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @Classname UserDaoTest
* @Description TODO
* @Date 2020/12/24 22:18
* @Created by mmz
*/
public class UserDaoTest {
@Test
public void test(){
// 创建sqlsession对象
SqlSession sqlsession = MybatisUtils.getSqlsession();
// 得到mapper 方式一
UserDao mapper = sqlsession.getMapper(UserDao.class);
// 方式二
List<User> userList = mapper.getUserList();
for (User user : userList){
System.out.println(user);
}
// 关闭sqlsession
sqlsession.close();
}
}
编写UserDao的mapper文件
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mmz.dao.UserDao">
<!-- 配置查询所有-->
<select id="getUserList" resultType="com.mmz.pojo.User">
select * from user;
</select>
</mapper>
小结 比较mybatis和传统的jdbc
首先,我们获得一个叫sqlsession的东西,sqlsession可以根据我们写的userdao这个类对象来获得相应的mapper对象,然后通过这个对象来调用方法。
UserDao mapper = sqlsession.getMapper(UserDao.class);
当然sqlsession是如何知道这个mapper,需要在mybatis-config.xml中配置
<mappers>
<mapper resource="com/mmz/dao/UserMapper.xml"></mapper>
</mappers>
最后我们得到这个mapper,调用userdao接口中的方法。那方法之所以能执行sql语句,是因为mapper.xml中已经写好了sql语句。mapper.xml就是写了sql语句,与绑定的userdao
<mapper namespace="com.mmz.dao.UserDao">
<!-- 配置查询所有-->
<select id="getUserList" resultType="com.mmz.pojo.User">
select * from user;
</select>
</mapper>
基础CRUD
namespace 命名空间
namespace中的包名和Dao/Mapper接口名称一致。
select
select标签相当于查询语句
id就是对应的namespace中的方法
resultType sql语句中执行的返回值
还可能有paramterType 参数类型
根据用户id查找用户
当然我们只需要增加userdao中接口的方法
// 根据id查询用户
User getUserById(int id);
同样的,我们需要在usermapper.xml中增加select标签,添加查询的sql语句
<select id="getUserById" resultType="com.mmz.pojo.User" parameterType="int">
select * from user where id = #{id}
</select>
最后增加test测试方法
@Test
public void testFindUserById(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
// 得到mapper 方式一
UserDao mapper = sqlsession.getMapper(UserDao.class);
User userById = mapper.getUserById(1);
System.out.println(userById);
}
增加一个用户
xml配置文件中添加
<!--对象中的属性可以直接取出来-->
<insert id="addUser" parameterType="com.mmz.pojo.User" >
insert into user (id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
测试代码
// 增删改需要提交事务
@Test
public void testAddUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
User user = new User(4,"duoduo","123");
int i = mapper.addUser(user);
System.out.println(i);
sqlsession.commit();
sqlsession.close();
}
记住必须要记住,除了查询之外,增删改数据库,需要提交事务。
修改更新
xml配置文件
<update id="updateUser" parameterType="com.mmz.pojo.User">
update user set name = #{name},pwd=#{pwd} where id = #{id};
</update>
测试类代码
// 修改更新User
@Test
public void testUpdateUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
User user = new User(4,"duoduo123","123");
int i = mapper.updateUser(user);
System.out.println(i);
sqlsession.commit();
sqlsession.close();
}
删除用户
xml配置文件
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id};
</delete>
测试类
// 删除修改User
@Test
public void testDeleteUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
int i = mapper.deleteUser(4);
System.out.println(i);
sqlsession.commit();
sqlsession.close();
}
万能map
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当会考虑Map
xml配置文件
<!--传递map中的key,这样解决了如果User类有过多属性-->
<insert id="addUser2" parameterType="map" >
insert into user (id, name, pwd) values (#{userid},#{username},#{userpwd});
</insert>
// 增删改需要提交事务
@Test
public void testAddUser2(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("username","xiaoqi");
map.put("userpwd","jinmao");
int i = mapper.addUser2(map);
System.out.println(i);
sqlsession.commit();
sqlsession.close();
}
Map传递参数,直接在sql中取出key即可
对象传递参数,直接在sql中取出对象的属性即可,
只有一个基本类型参数的情况下, 可以默认在xml配置文件中不写
多个参数如果用到了对象,需要写上全限定类名
当然还有注解的方式
模糊查询
1.java代码执行的时候,传递通配符%
List<User> m = mapper.getUserLike("%m%");
这样不会产生sql注入,需要在java方法里面去写
2.在sql拼接中使用通配符,这样会产生sql注入
select * from user where name like “%”#{value}“%”;
mybatis的配置解析
核心配置文件mybatis-config.xml
官方建议使用这个名字
环境配置environments
mybatis可以配置多种环境配置,
<environments default="mysql">
<environment id="mysql">
<!-- 事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置数据库的四个基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatismmz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8"></property>
<property name="username" value="root"></property>
<property name="password" value="password"></property>
</dataSource>
</environment>
</environments>
可以在这个environments属性中的default属性中设置选择的环境配置
事务管理器
<transactionManager type="JDBC"></transactionManager>
在mybatis有两种类型的事务管理器JDBC/MANAGED
数据源
连接数据库
dbcp cp30 druid
有三种类型内建的数据库 UNPOOLED POOLED JNDI
UNPOOLED
这个数据源的实现只是每次请求的时候打开和关闭连接,虽然有点慢。但是对于在数据库连接可用性方面没有太高的要求的简单程序,是一个很好的选择。
属性properties
可以通过属性来引用配置文件
这些属性都是外部可以动态替换的,既可以在典型的java属性文件中配置,亦可以通过properties元素的子元素进行传递
编写db.propeties的配置文件
dirver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatismmz?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username=root
password=password
在核心配置文件中引入
<properties resource="db.properties"></properties>
<environments default="mysql">
<environment id="mysql">
<!-- 事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置数据库的四个基本信息-->
<property name="driver" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</dataSource>
</environment>
</environments>
优先使用外部配置文件
类型别名
类型别名是java类型设置一个短的名字,它只和xml配置有关,存在的意义仅在于用来减少类完全限定类名的冗余
第一种方式自己配置
<!--可以给实体类起别名-->
<typeAliases>
<typeAlias type="com.mmz.pojo.User" alias="User"></typeAlias>
</typeAliases>
第二种指定包名
在每一个包中的javabean,在没有注解的情况下面,会使用Bean的首字母小写的非限定类名来作为他的别名,若有注解,为注解的值@Alias(“author”)
<!--可以给实体类起别名-->
<typeAliases>
<package name="com.mmz.pojo"/>
</typeAliases>
总结
实体类少,建议使用第一种
实体类多,建议使用第二种
第一种可以diy,第二种无法自定义
当然还有一些默认别名,基本类和包装类
设置settings
这是mybatis中及其重要的调整设置,他们会改变mybatis的运行时的行为
映射器mappers
MapperRegistry:注册绑定我们的Mapper文件
第一种使用资源路径
<mappers>
<mapper resource="com/mmz/dao/UserMapper.xml"></mapper>
</mappers>
第二种使用class文件绑定注册
注意接口和mapper配置文件必须同名,接口和配置文件必须在同一个包下面
第三种使用package
<package name="com.mmz.dao"/>
使用扫描包注入的时候,与第二种一样,mapper配置文件与接口必须同名,接口和配置文件必须在同一个包下面
生命周期和作用域
sqlsessionfactorybuilder
一旦创建sqlsessionfactory,就不需要他了
局部变量
sqlsessionfactory
说白了就是数据库连接池
一旦被创建就应该在运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
单例模式
sqlsession
相当于数据库连接的一个线程请求
用完之后关闭,否则资源被占用
ResultMap结果集
解决属性名和字段名不统一的问题
现在我开了一个新的module,mybatis-03,把密码的pwd改成了password
进行查询,发现password=null
解决方案
在sql中起别名
我们所谓的resultMap
结果集映射resultMap
<resultMap id="UserMap" type="User">
<!--colunm是数据库中的字段,property实体类中的属性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="UserMap" parameterType="int">
select * from user where id = #{id};
</select>
要在UserDao.xml中写好resultMap标签,然后在下面的sql查询中,设置返回结果为ResultMap,也就是我们定义好的id
日志
日志工厂
如果一个数据库操作出现了异常,我们需要排错,日志就是最好的助手。
曾经:sout,debug
现在:日志工厂
具体使用哪个日志工厂,是mybatis可选择
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j
先导入log4j的包
log4ja是apache的一个开源项目,通过使用log4j,我们可以控制日志信息输送的目的是控制台,文件,gui组件
我们可以通过配置文件来灵活配置,不需要修改应用代码
配置文件log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=./log/mmz.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
分页
为什么要分页?
减少数据的处理量
使用limit分页
sql中的limit分页
select * from user limit startIndex,pageSize;
select * from user limit 0,2;
使用mybatis实现分页
接口
// 分页
List<User> getUserByLimit(Map<String,Integer> map);
配置xml文件
<!--分页实现-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from user limit #{startIndex},#{pageSize}
</select>
测试类
@Test
public void testLimit(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
hashMap.put("startIndex",0);
hashMap.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(hashMap);
for (User user : userByLimit) {
System.out.println(user);
}
sqlsession.close();
}
注解开发
接口
package com.mmz.dao;
import com.mmz.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @Classname UserMapper
* @Description TODO
* @Date 2021/1/2 7:11
* @Created by mmz
*/
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
}
配置文件
<!--绑定接口-->
<mappers>
<mapper class="com.mmz.dao.UserMapper"></mapper>
</mappers>
测试类
package com.mmz.dao;
import com.mmz.pojo.User;
import com.mmz.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import sun.rmi.runtime.Log;
import java.util.HashMap;
import java.util.List;
/**
* @Classname UserDaoTest
* @Description TODO
* @Date 2020/12/26 16:47
* @Created by mmz
*/
public class UserDaoTest {
static Logger logger = Logger.getLogger(UserDaoTest.class);
@Test
public void getUserList(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
User userList = mapper.getUserById(1);
System.out.println(userList);
sqlsession.close();
}
@Test
public void testLog4j(){
logger.info("info 进入了test");
logger.debug("info 进入了test");
logger.error("info 进入了test");
}
@Test
public void testLimit(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserDao mapper = sqlsession.getMapper(UserDao.class);
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
hashMap.put("startIndex",0);
hashMap.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(hashMap);
for (User user : userByLimit) {
System.out.println(user);
}
sqlsession.close();
}
}
执行过程
Resources获取加载全局配置文件
实例化SqlSessionBuilder构造器
解析配置文件流XMLConfigBuilder
Configuration所有的配置文件
SqlSessionFactory实例化
transaction事务管理器
executor执行器
创建sqlsession
实现crud
查看是否执行成功,可能回滚
执行成功,提交事务
关闭
注解crud
我们可以在工具类创建的时候,自动提交事务
编写mapper接口
package com.mmz.dao;
import com.mmz.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* @Classname UserMapper
* @Description TODO
* @Date 2021/1/2 7:11
* @Created by mmz
*/
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
// 方法存在多个参数,所有的参数前面必须加上@param注解
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
int addUser(User user);
@Update("update user set name =#{name},pwd=#{password} where id = #{id}")
int updateUser(User user);
@Delete("delete from user where id = #{id}")
int deleteUser(int id);
}
测试类测试
package com.mmz.usermapper;
import com.mmz.dao.UserMapper;
import com.mmz.pojo.User;
import com.mmz.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @Classname testUserMapper
* @Description TODO
* @Date 2021/1/2 7:13
* @Created by mmz
*/
public class testUserMapper {
@Test
public void test(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlsession.close();
}
@Test
public void testById(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlsession.close();
}
@Test
public void testAddUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
User user = new User(6,"hello","231231");
int i = mapper.addUser(user);
System.out.println(i);
sqlsession.close();
}
@Test
public void testUpdateUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
User user = new User(6,"xixi","231231");
mapper.updateUser(user);
sqlsession.close();
}
@Test
public void testDeleteUser(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
int i = mapper.deleteUser(5);
sqlsession.close();
}
}
关于@param注解
基本类型的参数或者String类型,需要加上
引用类型不需要加
如果只有一个基本类型,可以忽略,但是建议大家都加上