MyBatis

简介

MyBatis(原名iBatis)是持久层框架。主要是与数据库打交道

  • 避免JDBC和返回集合代码。利用xml文件操作。

maven仓库

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>
  • dao数据持久化:data access object就是将数据库中的数据,转化为持久状态

第一个MyBatis

搭建环境

  • maven导包mysql-connector-java,mybatis
  • 主配置文件,并注册mappers
  • 实体类、其接口和响应的映射.xml(实现sql操作)
  • 编写utils,MyTest

CRUD

  • maven导
  • pojo
public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
  • 方法和映射配置文件
public interface UserMapper {
    public List<User> selectUser();	//所有用户

    public User getUserById(int id);

    public int addUser(User user);

    public int upDateUser(User user);
    
    public int deleteUser(int id);
    
    int addUser2(Map<String,Object> map);

    List<User> getUserLike(String value);
}
================UserMapper.xml===========================
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0/EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.adair.dao.UserDao">		//链接UserDao
    <select id="selectUser" resultType="com.adair.pojo.User">//	其中的方法,和返回值
        select * from mybatis.user
    </select>
    
    <select id="getUserById" parameterType="int" resultType="com.adair.pojo.User">
        select * from mybatis.user where id= #{id}
    </select>
    
    <insert id="addUser" parameterType="com.adair.pojo.User">
        insert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd});
    </insert>
    
    <update id="upDateUser" parameterType="com.adair.pojo.User">
        update mybatis.user
        set  name=#{name},pwd=#{pwd}
        where id=#{id};
    </update>
    
    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id=#{id}
    </delete>
    
    <insert id="addUser2" parameterType="map">
        insert into mybatis.user (id,pwd) values (#{userid},#{passWord})
    </insert>
    
    <select id="getUserLike" parameterType="String" resultType="com.adair.pojo.User">
        select * from mybatis.user where name like #{value}
    </select>
</mapper>
  • 主配置文件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>
    <typeAliases>
        <package name="com.adair.pojo"/>
    </typeAliases>
    <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/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/adair/dao/UserMapper.xml"/>
    </mappers>

</configuration>
  • MyTest
public class MyTest {
    public static void main(String[] args) throws IOException {
        String resources = "mybatis-config.xml";            //可以设置为常用类。utils包
        InputStream is = Resources.getResourceAsStream(resources);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sessionFactory.openSession(true);

        //方式一:getMapper得到类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> userList = mapper.selectUser();//所有用户
        User user = mapper.getUserById(1);//id为1
        mapper.addUser(new User(5, "zhaoliu", "444"));//增加一个用户
        mapper.upDateUser(new User(5,"laowu","555"));   //更新用户
        mapper.deleteUser(5);

        //若数据库没有更变sqlSession.commit();

        System.out.println(user.toString());
        //方式二:直接得到方法,并利用方法
//        List<User> userList = sqlSession.selectList("com.adair.dao.UserDao.selectUser");

        HashMap<String, Object> map = new HashMap<>();//new hashmap然后put键值对
        map.put("userid",4);		
        map.put("passWord","444");
        mapper.addUser2(map);
        
        List<User> userList = mapper.getUserLike("%李%");
        for (User user : userList) {
            System.out.println(user.toString());
        }
    }
}
  • utils下的工具类
public class MybatisUtils {//直接优化成工具类
    private static SqlSessionFactory sqlsessionFactory;
    static {
        try {
            String resources = "mybatis-config.xml";
            InputStream is = Resources.getResourceAsStream(resources);
            sqlsessionFactory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        return sqlsessionFactory.openSession();
    }
}

传参总结:

  • 一个基本类型:直接可以用。标签内无parameterType也可以
  • 对象:直接利用属性即可
  • map:多个属性,可以利用parameterType=“map”。键值对

mybatis-config.xml标签

environment

  • transactionManager
  • dataSource

properties

==============db.properties================
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

==============mybatis-config.xml================
<configuration>
    <!--两种引入属性的方式:db.properties优先-->
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="pwd" value="123"/>
    </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="${pwd}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/adair/dao/UserMapper.xml"/>
    </mappers>

</configuration>

typeAliases

自定义、扫描包、注解:映射.xml文件返回的类型设置

<typeAliases>
    <typeAlias type="com.adair.pojo.User" alias="User"/>    <!--自定义别名-->
    <package name="com.adair.pojo"/>      <!--扫描包:下面所有类的别名都是小写-->
</typeAliases>

@Alias("hello")
public class User {
    private int id;
    private String name;
    private String pwd;
}
======================UserMapper.xml========================
<select id="selectUser" resultType="user">
     select * from mybatis.user
</select>

mappers注册

法一:通过资源引用。

<mappers>
    <mapper resource="com/adair/dao/UserMapper.xml"/>
</mappers>

法二:(同一包下。类名同.xml名字一样)

<mappers>
    <mapper class="com.adair.dao.UserMapper"/>
</mappers>

法三:(要求同二)

<mappers>
    <package name="com.adair.dao"/>
</mappers>

生命周期和作用域

sqlSessionFactoryBuilder:

  • 局部变量,创建sqlSessionFactory就可以挂了。

sqlSessionFactory:

  • 应用作用域,运行期间一直存在。相当于一个连接池

sqlSession:

  • 方法作用域(用完就关)。连接到连接池的一个请求。sqlSession的实例是不安全的,不可被共享。
image-20220507101238441

tip:代码正确(还报错),删除target下的。

属性≠字段

在java类的属性中会出现和数据库字段名字不同的情况(password≠pwd)。

解决方案:

  • 别名。在select时,给数据库字段起一个别名(同java属性)
<select id="getUserById" parameterType="int" resultType="com.adair.pojo.User">
    select id,name,pwd as password from mybatis.user where id= #{id}
</select>
  • resultMap
<resultMap id="UserMap" type="User">
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>


<select id="getUserById" resultMap="UserMap">
    select * from mybatis.user where id= #{id}
</select>

log

STDOUT_LOGGING

  • mybatis-config.xml
<settings>		//在MyTest会自动运行,并显示
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
  • 控制台

image-20220505092406496

LOG4J

  • mybatis-config.xml
<settings>		//在MyTest会自动运行,并显示
    <setting name="logImpl" value="LOG4J"/>
</settings>
  • 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/adair.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
  • Logger.getLogger
#MyTest
static Logger logger = Logger.getLogger(MyTest.class);
@Test
public void test(){
    logger.info("info:进入logger");
    logger.debug("debug:进入logger");
    logger.error("error:进入logger");
}
  • 在项目下,自动生成log/adair.log文件

分页

limit

  • 在sql
select * from user limit 0,2
  • 接口和映射配置文件
public interface UserMapper {
    List<User> getUserByLimit(Map map);
}

=======================================================
<select id="getUserByLimit" parameterType="map" resultType="user">
	select * from user limit #{startIndex},#{pageSize}
</select>
=======================================================
    public static void main(String[] args) throws IOException {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("startIndex",1);
        map.put("pageSize",2);

        List<User> userList = mapper.getUserByLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
    }

RowBounds

  • 借用getUser方法(之前的)得到所有用户
List<User> list = sqlSession.selectList("com.adair.dao.UserMapper.getUser", null, new RowBounds(1, 2));
for (User user : list) {
    System.out.println(user);
}

使用注解开发

  • UserMapper接口的方法
@Select("select * from mybatis.user where id=#{id}")
public User getUserById(int id);
  • 主配置文件用mappers注册就好了。最好用的
<mappers>
    <mapper class="com.adair.dao.UserMapper"/>
</mappers>
  • MyTest。同上

本质:反射机制。底层:动态代理

  • Debug查看本质
image-20220505142514251
  • Mybatis执行流程

    image-20220505142438907
  • 利用注解select,param

@Select("select * from mybatis.user where id=#{id1}")
public User getUserById(@Param("id1") int id);                  
  • 没有提交到数据库的情况
sqlsessionFactory.openSession();		//设置为true
//或者,手动提交
sqlSession.commit();		
  • #{}和${}的区别是什么

    a、#{}是预编译处理(化为?),${}是字符串替换。
    d、使用#{}可以有效的防止 SQL 注入,提高系统安全性

Lombok

偷懒神器

步骤:

  • 在idea安装plugin
  • 导入maven依赖
  • 在类上加注解即可
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

多对一association

法一:查询嵌套

  • 导入数据库student ,teacher
  • pojo类。用lombok的话,安装插件和库
@Data
public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}
  • dao
public interface StudentMapper {
    public List<Student> getStudent();
}
  • 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>
	//其他的

    <mappers>		//导入资源
        <mapper class="com.adair.dao.StudentMapper"/>
        <mapper class="com.adair.dao.TeacherMapper"/>	
    </mappers>
</configuration>
  • resources下的com.adair.dao。联表查询
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0/EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.adair.dao.StudentMapper">
    <select id="getTeacher" resultType="teacher">
        select * from teacher
    </select>

    <resultMap id="StudentTeacher" type="student">
        <association property="teacher" column="id" javaType="Teacher" select="getTeacher"/>
    </resultMap>


    <select id="getStudent" resultMap="StudentTeacher">
        select * from student
    </select>
</mapper>
  • 测试查询

法二:结果嵌套(直接sql,推荐)

  • pojo
public interface StudentMapper {
    public List<Student> getStudent();
    public List<Student> getStudent2();
}
  • StudentMapper.xml。assciation用的javaType
<mapper namespace="com.adair.dao.StudentMapper">

    <resultMap id="StudentTeacher2" type="student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
            <result property="id" column="tid"/>
        </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>
</mapper>

一对多collection

  • List getTeacher();方法。 colletion中的ofType
<resultMap id="TeacherStudent" type="teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
    </collection>
</resultMap>

<select id="getTeacher" resultMap="TeacherStudent">
    select t.name tname,t.id tid,s.name sname,s.id sid 
    from student s,teacher t 
    where s.tid=t.id
</select>
  • List getTeacher2();方法。按查询嵌套
<select id="getTeacher2" resultMap="TeacherStudent2">
    select * from teacher
</select>

<resultMap id="TeacherStudent2" type="Teacher">
    <collection property="students"  javaType="ArrayList" ofType="Student" select="getStudent" column="id"/>	//column也可为name
</resultMap>


<select id="getStudent" resultType="student">
    select * from student
</select>

动态SQL

动态SQL就是根据不同的情况,生成不同的SQL语句

实验

  • utils包。Universally unique identifier
public class IDUtils {
    public static String getId(){
        return UUID.randomUUID().toString().replace("-","");
    }
}
  • 配置文件
<?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>
    <properties resource="db.properties"/>
    
    <settings>
        
    </settings>

    <typeAliases>
        <package name="com.adair.pojo"/>
    </typeAliases>

    <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 class="com.adair.dao.BlogMapper"/>
    </mappers>
</configuration>
  • 实体类,接口方法,其映射的.xml
===============pojo=================
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}
=============dao层BlogMapper===================
public interface BlogMapper {
    int addBlog(Blog blog);
    List<Blog> queryBlogIf(Map<Object, Object> map);
}

if

==============BlogMapper.xml==================
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0/EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.adair.dao.BlogMapper">
    <select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from blog where
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </select>
</mapper>

where

  • 上面的if在一定条件下会报错(and多余错误) 。where标签可以避免
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
           and author = #{author}
        </if>
    </where>    
      
</select>

choose

  • choose(when,otherwise)。相当于switch
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                author = #{author}
            </when>
            <otherwise>
                views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

set

  • set标签,会动态的去掉逗号
    <update id="updateBlog" parameterType="map">
        update blog
        <set>
            <if test="title != null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author}
            </if>
        </set>
        <where>
            id = #{id}
        </where>
    </update>

sql标签

  • 使用公共部分的sql语句
<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>
  • 注意:片段基于单表。片段不包括where标签

foreach

<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="(" close=")" separator=" or ">
            id=#{id}
        </foreach>
    </where>
</select>
public void test1() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<Object, Object> map = new HashMap<>();
    ArrayList<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    map.put("ids",ids);
    for (Blog blog : mapper.queryBlogForeach(map)) {
        System.out.println(blog);
    }
}

缓存

读写分离,主从复制

一级缓存

也叫本地缓存

public class MyTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user1 = mapper.getUserById(2);
        System.out.println(user1);
        User user2 = mapper.getUserById(2);
        System.out.println(user2);
        System.out.println(user1 == user2);
        sqlSession.close();
    }
}
  • 结果
image-20220506211340617
  • 在其中增加一个update,更新语句,则会更新缓存。地址不同则会false
image-20220506212834858
  • 在两个查询语句中间加入sqlSession.clearCache();
image-20220506212959391

小结:一级缓存默认开启,只在一次qlSession中有效。

二级缓存

也叫全局缓存。由于一级缓存太低,所有诞生了二级缓存。

  • 主配置文件
<setting name="cacheEnabled" value="true"/>
  • 类配置文件。注意:类要实现序列化
<cache readOnly="true"/>	//true为同一个实体类。为false则是一个拷贝的
<select id="getUserById" resultType="user" useCache="true">
    select * from user where id=#{id}
</select>
  • MyTest
public class MyTest {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        SqlSession sqlSession2 = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user1 = mapper.getUserById(1);
        System.out.println(user1);
        sqlSession.close();

        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = mapper2.getUserById(1);
        System.out.println(user2);

        System.out.println(user1 == user2);

        sqlSession2.close();
    }
}
  • 结果
image-20220506220914114

缓存原理

  • 查看顺序:用户 → 二级缓存 → 一级缓存 → 数据库
  • 提示: 一级缓存提交或关闭之后,才会提交到二级缓存中。不关闭则不提交

自定义缓存ehcatch

  • 接口映射的配置文件
<cache readOnly="true" type="org.mybatis.caches.ehcache.EhcacheCache"/>
  • ehcache.xml
<?xml version="1.0" encoding="UTF-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>

面试高频:

  • MySql引擎

  • InnoDB底层原理

  • 索引

  • 索引优化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值