Mybatis入门,基础案例,原理,注意事项(入门基础款)

mybatis概述、环境搭建、入门案例、自定义mybatis框架

mybatis概述

mybatis是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql语句本身, 而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis通过xml 或注解的方式将要执行的各种statement配置起来,并通过java对象和statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并 返回。
采用 ORM (Object Relational Mappging 对象关系映射 把数据库表和实体类及实体类的属性对应起来,可以操作实体类就实现操作数据表)思想解决了实体和数据库映射的问题,对 jdbc进行了封装,屏蔽了 jdbc api 底层访问细节,使我 们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

入门案例

  1. 创建maven工程并导入坐标
  2. 创建实体类和dao接口
  3. 创建Mybatis的主配置文件
    SqlMapConfig.xml
  4. 创建映射配置文件
    IUserDao.xml
    注意事项:
    1. 创建IUserDao.xml和IUserDao.java ,名称是为了和之前保持一直。
      在Mybatis中,他把持久层的操作接口名称和映射文件也叫做Mapper
      所以 IUserDao 和 IUserMapper 是一样的
    2. 在 idea中创建目录时,和包不同
      包:com.lx.dao 是三级结构
      目录:com.lx.dao 是一级结构
    3. Mybatis的映射文件位置必须和dao 接口的包结构相同
    4. 映射配置文件的 mapper 标签 namespace 属性的取值必须是 dao 接口的全限定类名
    5. 映射配置文件的操作配置(select),id 属性的取值必须是 dao 接口的方法名
      当我们遵从了3、4、5点之后,在开发中就无需再写 dao 类

mybatis的入门案例

  1. 读取配置文件

  2. 创建SqlSessionFactory工厂

  3. 创建SqlSession

  4. 创建Dao接口的代理对象

  5. 执行dao中的方法

  6. 释放资源

    注意事项:
    在映射配置中告知mybatis要封装到哪个实体类中
    配置方式:指定实体类的全限定类名123456

mybatis基于注解的入门案例:

把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并指定SQL语句
同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名
注意:
在实际开发中,都是越简便越好,所以都采用不写dao实现类的方式,不管使用XML还是注解配置
但是Mybatis支持写dao的方式

//1.读取配置文件
InputStream in= Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory=builder.build(in);
//3.使用工厂生产 SqlSession 对象
SqlSession session=factory.openSession();
//4.使用sqlSession创建 dao接口的代理对象
IUserDao userDao=session.getMapper(IUserDao.class);
//5.使用代理对象执行方法
List<User> users=userDao.findAll();
for (User user:users){
    System.out.println(user.toString());
}
//6.释放资源
session.close();
in.close();

自定义mybatis

比如需要实现查询所有:创建代理对象,实现查询所有

  1. 查询所有实现在工具类中Executor.java
  2. 调用工具类实现,在创建dao实现类增强时调用
  3. 创建代理对象DefaulSqlSession.java时创建dao实现类
  4. 调用,在DefaulSqlSession中得MapperProxy调用
  5. MapperProxy.java,从某处找出要执行的语句和封装的结果。从配置文件找。怎么读取?通过工具类XMLConfigBuilder来读取。读取后需要存,存在Configuration对象中

配置文件配置jdbc

<configuration>
    <properties resource="jdbcConfig.properties">
        <!-- 配置properties,可以在标签内部配置连接数据库的信息,也可以通过属性引用外部配置文件的信息
            resources属性:指定配置文件的位置,按照类路径的写法来写,并且必须存在于类路径下。
        -->
      <!--  <property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="123"/>-->
    </properties>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql环境 -->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

jdbcConfig.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
jdbc.username=root
jdbc.password=123

mabatis基本使用、单表CRUD操作、参数和返回值、dao的编写、配置细节(标签的使用)

mybatis自定义和环境搭建+完善自定义Mybatis的注解开发

  1. 有一个SqlSessionFactoryBuilder来接收SqlMapConfig.xml文件流,构建出SqlSessionFactory对象(SqlSessionFactory本身不能操作数据库,借助生产的SqlSession来进行操作)
  2. SqlSessionFactory读取SqlMapConfig.xml中的连接数据库和mapper映射信息。用来生产出真正操作数据库的SqlSession对象
  3. SqlSession对象两大功能:生产接口代理对象,定义通用增删改查方法。除了数据库连接信息,还需要得到sql语句
  4. (生产接口代理对象)首先创建一个dao接口的代理实现类,代理实现类中调用增删改查方法来实现功能
  5. (定义通用增删改查方法)写dao实现类,直接调用增删改查的方法
  6. 封装结果集

简单的CRUD操作

IUserDao.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.lx.dao.IUserDao">
    <select id="findAll" resultType="com.lx.domain.User">
        select * from user;
    </select>
    <insert id="saveUser" parameterType="com.lx.domain.User">
            <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
                select last_insert_id();
            </selectKey>
        insert into user (username,address,sex,birthday)values (#{username},#{address},#{sex},#{birthday});
    </insert>
    <update id="updateUser" parameterType="com.lx.domain.User">
        update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id =#{id};
    </update>
    <delete id="deleteUser" parameterType="Integer"> 
    	delete from user where id=#{id}
    </delete>
    <select id="findById" parameterType="Integer" resultType="com.lx.domain.User">
        select * from user where id=#{id}
    </select>
    <select id="findByName" parameterType="String" resultType="com.lx.domain.User">
        <!-- select * from user where like #{String}  Statement对象的字符串拼接sql-->
        select * from user where like '%${value}%'<!-- PrepatedStatement的参数占位符 -->
    </select>
    <select id="findTotal" resultType="int">
        select count(id) from user;
    </select>

</mapper>

IUserDao.java

package com.lx.dao;

import com.lx.domain.User;

import java.util.List;

public interface IUserDao {
    public List<User> findAll();
    void saveUser(User user);
    void updateUser(User user);
    void deleteUser(Integer id);
    User findById(Integer id);
    List<User> findByName(String username);
    int findTotal();
}

SqlMapConfig.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">
        <!-- 配置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/mybatis?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>


    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="com/lx/dao/IUserDao.xml"></mapper>
    </mappers>
</configuration>

test类

package com.lx.test;

import com.lx.dao.IUserDao;
import com.lx.domain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class MybatisTest {
    private InputStream in;
    private SqlSession session;
    private IUserDao userDao;
    @Before
    public void init() throws  Exception{
        in= Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in);
        session=factory.openSession();
        userDao=session.getMapper(IUserDao.class);
    }
    @After
    public void destroy()throws Exception{
        in.close();
        session.close();
    }
    @Test
    public void testFindAll() {

        List<User> users= userDao.findAll();
        for (User user:users){
            System.out.println(user);
        }

    }
    @Test
    public void testSave() {
        User user=new User();
        user.setUsername("mybatis saveuser");
        user.setAddress("mybatis");
        user.setSex("n");
        user.setBirthday(new Date());



        userDao.saveUser(user);
        //提交事务
        session.commit();

    }
    @Test
    public void testUpdate(){
        User user=new User();
        user.setUsername("mybatis saveuser");
        user.setAddress("mybatis");
        user.setSex("n");
        user.setId(5);
        user.setBirthday(new Date());
        userDao.updateUser(user);

    }
    @Test
    public void testDelete(){
        userDao.deleteUser(6);

    }
    @Test
    public void testFindOne(){
        userDao.findById(7);

    }
    @Test
    public void testFindByName(){
        //select * from user where like #{String}
        //List<User> users=userDao.findByName("%王%");

        //select * from user where like '%${value}%'
        List<User> users=userDao.findByName("L");
        for (User user:users){
            System.out.println(user);
        }

    }
    @Test
    public void testFindTotal(){
        int count=userDao.findTotal();
        System.out.println(count);

    }

}

数据字段问题:

mysql数据库在win系统下不区分大小写,Linux严格区分大小写
在win下 userName = username,userId != id
如何解决?
想办法进行匹配

select * from user;

从sql层面来解决

select id as userId,username as userName,address as userAddress,sex as userSex,
	brithday as userBrithday from user

也可以采用配置的方式

<!-- 配置 查询结果的列名和实体类的属性名对应关系 -->
<resultMap id="userMap" type="com.lx.domain.user">
    <!--主键字段对应-->
    <id property="userId" column="id"></id>
    <!--非主键字段对应-->
    <result property="userName" column="username"></result>
    <result property="userAddress" column="address"></result>
    <result property="userSex" column="sex"></result>
    <result property="userBirthday" column="birthday"></result>
</resultMap>
 <!--使用结果类型定义时-->
 //<!--<select id="findAll" resultType="com.lx.domain.User">-->
 <select id="findAll" resultMap="userMap">
    select * from user;
</select>
 <!--执行效率慢了,开发效率提高-->

mybatis的连接池、事务控制,深入和多表,多表查询(一对多,一对一,多对多)

连接池

连接池:

  • 可以减少连接的次数,连接池就是一个存储连接的一个容器。其实就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一个连接,该集合还必须保证队列的特性:先进先出。

mybatis的连接池:

  • mybatis连接词提供了三种方式的配置:
    • 配置的位置:
      • 主配置文件sqlMapConfig.xml中的dataSource标签,type属性表示采用何种连接池方式。
    • type属性的取值:
      • POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
      • UNPOOLED 采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource的接口,但是并没有实现池的思想。
      • JNDI 采用服务器提供的JNDI技术,来获取DataSource对象,不同的服务器所能拿到的DataSource不一样的。如果不是web或者maven的war工程,是不能使用的。tomcat中采用的连接池就是dbcp连接池

在这里插入图片描述
在这里插入图片描述
从池中获取一个连接,用完归还
在这里插入图片描述

事务控制

Mybatis中的事务:

  • 什么是事务
  • 事务的四大特性ACID
    • 原子性
    • 一致性
    • 隔离性
    • 持久性,具体不解释了,数据库原理的内容。
  • 不考虑隔离性会产生的三个问题
    • 脏读
    • 不可重复读
    • 虚读
  • 四种隔离级别
    • 读取未提交内容
    • 读取提交内容
    • 可重读
    • 可串行化

通过sqlSession对象的commit和rollback方法实现事物的提交和回滚

动态sql语句

直接附上代码

<!--IUserDao.xml-->
 </resultMap>
 <!-- 抽取重复的sql语句-->
 <sql id="defaultUser">
     select * from user
 </sql>

 <select id="findAll" resultMap="userMap">
   <include refid="defaultUser">

   </include>
 </select>
 <select id="findById" parameterType="Integer" resultMap="userMap">
     select * from user where id=#{id}
 </select>
 <select id="findByName" parameterType="String" resultMap="userMap">
     <!-- select * from user where like #{String}  Statement对象的字符串拼接sql-->
     select * from user where like '%${value}%'<!-- PrepatedStatement的参数占位符 -->
 </select>
 <!-- 根据queryVo的条件查询用户-->
 <select id="findByVo" parameterType="com.lx.domain.QueryVo" resultMap="userMap">
     <!-- select * from user where like #{String}  Statement对象的字符串拼接sql-->
     select * from user where like #{user.username}<!-- PrepatedStatement的参数占位符 -->
 </select>

<!-- <select id="findUserByCondition" resultMap="userMap" parameterType="user">
     select * from user where 1 = 1
     <if test="username != null">
         and  username=#{username}
     </if>
     <if test="sex!=null">
         and sex=#{sex}
     </if>
 </select>-->

 <select id="findUserByCondition" resultMap="userMap" parameterType="user">
     select * from user
     <where>
         <if test="username != null">
             and  username=#{username}
         </if>
         <if test="sex!=null">
             and sex=#{sex}
         </if>
     </where>
 </select>
 <!-- 根据queryVo中的 id集合 实现查询用户列表-->
 <select id="findUserByIds" resultMap="userMap" parameterType="queryvo">
     select * from user
     <where>
         <if test="ids!=null and ids.size()>0">
             <foreach collection="ids" open="and id in(" close=")" item="id" separator=",">
                 #{id}
             </foreach>
         </if>
     </where>
 </select>

测试类

//MybatisTest
@Test
public void testFindByCondition(){

    User user=new User();
    user.setUsername("江安河");
    user.setSex("男");
    List<User> users=userDao.findUserByCondition(user);
    for (User userr:users){
        System.out.println(userr);
    }

}
/**
 * 测试foreach标签的使用
 */
@Test
public void testFindInIds(){

    QueryVo vo=new QueryVo();
    List<Integer> list=new ArrayList<Integer>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    vo.setIds(list);
    List<User> users=userDao.findUserByIds(vo);
    for (User userr:users){
        System.out.println(userr);
    }

}

User类、

package com.lx.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

QueryVo

package com.lx.domain;

public class QueryVo {
    private User user;


    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

多表查询

为什么需要多表查询?

  • 比如一个用户有多个订单,一个订单可能有多个商品,一对多
  • 多个订单是一个用户下的,今天十个订单,其中三个是同一个人下的,显示就是3-1,1-1,1-1……,多对一

实现如下:
一张USER表,内容为:TEL_NUMBER,USERNAME,SEX,ID之类的
也就是一个手机号,这个人交啥,什么性别,分配了个ID。
需要一个中间表,比如一张表,内容为:TEL_NUMBER,QQ
如何理解呢?手机号唯一,缺点了同一个人,也可以是身份证号之类的唯一标识。这个人有多个QQ号(也可以是游戏角色之类的)。
还需要一张QQ号表,内容为:QQ,SEVER,LEVEL。
如何理解呢?一个QQ号,在一个游戏区里,有一个等级为?的角色

如果要查询一个人他所有的游戏角色,就只需要调用多表查询,由TEL_NUMBER来通过中间表查出他的所有游戏角色表。
为什么不把所有数据写道一张表里呢?会导致一张表非常的笨重臃肿,

代码实现如下

SELECT * FROM QQ号表 qq 
LEFT OUTER JOIN 中间表 uq ON qq.TEL_NUMBER = uq.TEL_NUMBER
LEFT OUTER JOIN 用户表 u ON u.id=uq.uid

输出基本靠吼

延迟加载

延迟加载:

  • 在真正使用数据时才发起查询,不用的时候不查询,按需加载、懒加载。
    对应的,也有立即加载。

比如一个憨憨,创建了一千个游戏角色,他一查QQ,你要不要把一千个游戏角色给他看?还是,他要看的时候再给他看。
好处:节约资源
需要在SqlMapConfig.xml配置,配置在中

<!-- 配置参数 -->
<settings>
    <!--配置开启全局延迟加载的开关-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载-->
    <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

QQ号Dao.xml也需要进行相关配置

<association property="" column="" javaType="" select="第二次调用查询的方法的全限定类名+“.”+方法名">
       </association>

对原有的select语句进行拆分,以前是外连接,现在是查询两次

 <select id="findAll" resultMap="userAccountMap">
       SELECT * FROM USER u LEFT OUTER JOIN account a ON u.id=a.uid
    </select>
    <select id="findById" parameterType="Integer" resultType="user">
        select * from user where id=#{id}
    </select>

效果如下、

  • 需要查询所有数据的时候
    在这里插入图片描述
  • 不需要查询所有数据的时候
    在这里插入图片描述
    可以看到,当把Account输入信息给注释的时候,并不会发生第二次查询

缓存

Mybatis中有一级缓存和二级缓存,缓存就是存在内存里的东西,拿的快,占内存资源。 目的是减少和数据库的交互次数。理论上内存够大的话,是可以把整个数据库缓存进去的。
有的数据不适用于缓存,比如经常改变却很重要的。比如股票价格。
一级缓存:

  • 对 SqlSession 对象的缓存。
  • 执行查询之后,查询结果会同时存入到SqlSession提供的一块区域中该区域是一个Map,当我们再次查询同样的数据,mybats会先去sqlsession中查询是否有,有的话直接用
  • 当SqlSession对象消失时,mybatis的一级缓存也消失
  • 当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存

二级缓存:

  • 它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创
    建的SqlSession共享其缓存。
  • 二级缓存的使用步骤:
    • 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    • 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
    • 第三步:让当前的操作支持二级缓存(在select标签中配置)

注解的方式开发:

代码一看便知

package com.lx.dao;

import com.lx.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

/**
 * mybatis中,CRUD有四个注解
 * @Select @Insert @Update @Delete
 */
@CacheNamespace(blocking = true)
public interface IUserDao {
    @Select(value = "select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "id",property = "accounts",
                    many = @Many(select = "com.lx.dao.IAccountDao.findAccountByUid",
                            fetchType = FetchType.LAZY))
    })
    List<User> findAll();

    @Select("select * from user where id=#{id}")
    @ResultMap(value = {"userMap"})
    User findById(Integer userId);

    //@Select("select * from user where username like #{username}")
    @Select("select * from user where username like '%${value}%' ")
    @ResultMap("userMap")
    List<User> findUserByName(String name);

}

方法的测试和以前相同
io流读取配置文件——SqlSessionFactory——工厂生产SqlSession——session获得dao——dao调用方法

给位,一起冲鸭

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值