MyBatis

MyBatis持久层框架

JDBC操作中会有大量的费管代码—工具类

映射问题

获取连接用时占操作数据库表时间的一半以上的时间。

  • Connection属于线程不安全的对象

  • 享元模式—连接池

    虽然没有办法减少单次使用成本,但是可以明显降低平均使用成本

引入MyBatis

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。

MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象) 为数据库中的记录。

ORM对象关系映射,解决编程中的对象模型和关系模型的阻抗不匹配性,达到以操作对象的方式操作关系型数据库

中文参考网站: MyBatis中文网

1、添加依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.13</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
</dependency>

2、创建表

create table tb_users(
    id bigint primary key auto_increment,
    username varchar(32) not null unique,
    password varchar(32) not null,
    birth datetime default now(),
    sex boolean default 1
)engine=innodb default charset utf8;

插入测试数据

insert into tb_users(username,password) values('yanjun','123456');
insert into tb_users(username,password) values('tanjie','333333');
insert into tb_users(username,password) values('xiaozhe','66666');

3、添加MyBatis的核心配置文件

实际上针对核心配置文件的名称和位置没有强制要求,但是一般位于resources目录下,名称为mybatis-config.xml

核心配置文件中主要负责:

  • 配置数据源。
  • 配置运行时参数
  • 注册映射元文件
<?xml version="1.0" encoding="UTF-8" ?><!-- 用于声明当前xml文件所支持的版本号和对应的编码字符集 -->
<!-- 声明当前xml文件所遵循的语法标准和对应的标签定义 -->
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 核心配置文件的根标签 -->
<configuration>
    <!-- 应用环境配置,可以针对不同的阶段定义不同的配置,多个配置使用id进行区分,默认使用哪个配置通过default进行指定 -->
    <environments default="yan">
        <!-- 某个具体的配置,这里使用id进行区分。允许定义多个,不同配置使用不同的id -->
        <environment id="yan">
            <!-- 针对不同的运行环境需要配置2方面的内容,分别是事务管理器和连接池 -->
            <!-- transactionManager用于配置事务管理器,这里可以定义的type有2种:如果使用的是本地事务type=JDBC,
            如果使用的是分布式事务则type=MANAGED 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整
            个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将
            closeConnection 属性设置为 false 来阻止默认的关闭行为  -->
            <transactionManager type="JDBC"/>
            <!-- 用于配置数据源。type有3种可选值
                UNPOOLED采用直连方式连接数据库,每次使用时临时获取连接,使用完成则关闭连接;
                POOLED采用MyBatis框架提供的一个简单连接池实现,实际开发中一般会替换为成品连接池,例如阿里的Druid;
                JNDI使用容器管理的连接池,例如使用tomcat提供的连接池
             -->
            <dataSource type="POOLED">
                <!-- 使用pooled连接池需要配置构建连接所需要的参数,例如驱动串、连接串、用户名称和口令,另外还有连接池相关配置,例如
                最大连接数、最大空闲连接数等 -->
                <!-- 定义配置的格式为 name为配置参数的名称,value为对应的配置参数值 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test3?serverTimezone=UTC"/>
                <property name="username" value="root" />
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

4、参照数据表定义对应的实体类,一般建议列名称和属性名称一致,可以考虑使用驼峰记法

@Data
public class User implements Serializable {
    private Long id;
    private String username;
    private String password;
    private Date birth;
    private Boolean sex;
}

5、定义映射元文件

事实上也没有什么强制规则,只是一般建议位于resources/mapper目录下,和类名称相关的方式进行命名。例如实体类名称为User,则映射元文件名称为UserMapper.xml

  • 映射关系,也就是类和表之间的对应关系、属性和列之间的对应关系,以及列或者属性的数据类型
    • 属性名称和列名称可以不一致,如果一致则可以使用框架提供的自动映射,以减少配置;如果不一致则必须进行配置
  • 定义执行增删改查所指定的sql语句
    • mybatis是一种半自动化ORM框架,以编写sql语句的工作量为代价换取高灵活性
    • hibernate是一种自动化ORM框架,无需编写sql语句,但是优化难度较高
<?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,用于通过引入名空间以保证当前配置文件中的所有命名的唯
一性。namespace一般和映射接口名称一致 -->
<mapper namespace="com.yan.dao.UserMapper">
    <!--  resultMap用于声明实体类和表之间的对应关系autoMapping="true"自动映射,
    要求实体类中的属性名称和列名称一致,自动支持驼峰计法。id用于区分多个不同的resultMap
    配置,type用于指定对应的实体类 -->
    <resultMap type="com.yan.entity.User" id="baseMapper">
        <!-- id用于配置主键和标识属性之间的对应关系,column为列名称,property为对应的属性名称,
        jdbcType用于指定对应的类型,对应的类型名称来自于java.sql.Types中定义的常量名称 -->
        <id column="id" jdbcType="BIGINT" property="id"/>
        <!-- result用于配置非键列和属性之间的关系,其中属性和id含义一致 -->
        <result column="username" property="username" jdbcType="VARCHAR"/>
        <result column="password" property="password" jdbcType="VARCHAR"/>
        <result column="birth" property="birth" jdbcType="DATE"/>
        <result column="sex" property="sex" jdbcType="BOOLEAN"/>
    </resultMap>
    <!--  查询使用select标签,插入insert标签,修改update标签,删除delete标签 -->
    <!-- 查询操作对应的sql语句配置,parameterType用于指定执行操作时的参数类型,
    resultMap用于指定获取结果时所使用的resultMap映射,从而达到将一行数据转换为一个值bean对象的目的 -->
    <select id="selectAll" resultMap="baseMapper">
        select * from tb_users
    </select>
</mapper>

定义映射元文件后必须到核心配置文件中进行注册,修改mybatis-config.xml添加配置

<!-- 用于注册映射元文件,可以注册多个 -->
<mappers>
    <!-- 一个mapper标签注册一个映射元文件,也可以使用包扫描减少配置个数 -->
    <mapper resource="mapper/UserMapper.xml"/>
</mappers>

6、通过MyBatis框架提供的API调用前面的相关配置完成数据库的查询操作

public class Test2 {
    public static void main(String[] args) throws Exception {
        // 构建用于读取核心配置文件的输入流,实际上使用getResourceAsReader和getResourceAsStream是等价的
//    Resources.getResourceAsStream(null)
        Reader r = Resources.getResourceAsReader("mybatis-config.xml");
        // 1、创建SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);
        // 2、通过SqlSessionFactory对象创建SqlSession对象,可以将SqlSession对象看作一个持久化管理器,通过它完成CRUD操作的调用
        SqlSession session = factory.openSession();
        // 3、通过SqlSession对象调用在映射元文件中配置的select操作
        // 参数1是对应的操作配置名称,就是映射元文件中的namespace+操作id两部分,参数2
        List<User> userList = session.selectList("com.yan.dao.UserMapper.selectAll");
//        常规的查询操作有2个方法,一个是只返回一个值的.selectOne,一个是返回多个值的selectList
        userList.forEach(System.out::println);  //使用lambda表达式输出显示查询结果
        //4、 关闭
        session.close();
//        一般针对factory不执行关系操作
    }
}

7、添加一个用户信息

7.1、修改映射元文件UserMapper.xml,添加对应的insert

<insert id="insert" parameterType="com.yan.entity.User">
        insert into tb_users(
        <!-- 因为有些属性不是必须有值,所以这里进行判断,如果传入参数对应的属性不为空,则表示当前属性对应的列需要插入数据 -->
        username,password  <!-- 数据库表中不允许username和password -->
        <!-- if标签用于实现判断,test中为一个boolean类型表达式,如果值为tru,则标签体会被执行 -->
        <if test="id!=null">,id</if>
        <if test="birth!=null">,birth</if>
        <if test="sex!=null">
            ,sex
        </if>
        ) values(
        #{username}  <!-- #{}用于获取参数类型中的指定列的对应值 -->
        ,#{password}
        <if test="id!=null">
            ,#{id}
        </if>
        <if test="birth!=null">
            ,#{birth,jdbcType=DATE}
        </if>
        <if test="sex!=null">
            #{sex}
        </if>
        )
    </insert>

编程调用

Reader r = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);
SqlSession session = factory.openSession();
User user=new User();
user.setUsername("小哲");
user.setPassword("888888");
session.insert("com.yan.dao.UserMapper.insert",user);
session.commit();
session.close();

8、删除数据

在具体的项目开发中一般不使用物理删除,而是使用逻辑删除

  • 物理删除实际上就是删除表中的数据

  • 逻辑删除实际上就是给需要删除的数据添加了一个删除标记,而不是真正的删除

    给用户表添加逻辑删除支持

    create table tb_users(
        id bigint primary key auto_increment,
        ...
        deleted boolean default 0
    );
    

    引入额外的列deleted用于表示本行数据已经被删除,所谓的删除的操作就是将当前行数据deleted赋值为1

修改映射元文件添加delete操作

<!-- 按照主键执行删除操作,参数类型为主键类型,增删改返回一个int类型数据,用于表示受影响行数 -->
<delete id="deleteById" parameterType="long">
    delete from tb_users where id=#{id}
</delete>

编码调用

Reader r = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);
SqlSession session = factory.openSession();
int len=session.delete("com.yan.dao.UserMapper.deleteById",4L);
if(len>0)
    System.out.println("删除成功");
else 
    System.out.println("删除失败!");
session.commit();
session.close();

9、按照主键加载对应的数据

<select id="selectById" parameterType="long" resultMap="baseMapper">
        select * from tb_users where id=#{id}
</select>

编程调用

Reader r = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);
SqlSession session = factory.openSession();
User user=session.selectOne("com.yan.dao.UserMapper.selectById",2L);
session.close();
System.out.println(user);

10、修改数据

按照主键修改其它的非空属性

<update id="updateById" parameterType="com.yan.entity.User">
    update tb_users set id=#{id}
<!-- test后面写的是属性名,就是user对象中的属性,两个尖括号之间的是列名,#{}之间的是属性名 -->
    <if test="username!=null">
        ,username=#{username}
    </if>
    <if test="password!=null">
        ,password=#{password}
    </if>
    <if test="birth!=null">
        ,birth=#{birth,jdbcType=DATE}
    </if>
    <if test="sex!=null">
        ,sex=#{sex,jdbcType=BOOLEAN}
    </if>
     where id=#{id}
</update>

编程调用

Reader r = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);
SqlSession session = factory.openSession();

User user=session.selectOne("com.yan.dao.UserMapper.selectById",2L);
user.setBirth(new Date(1989-1900,2-1,3));        
int updated=session.update("com.yan.dao.UserMapper.updateById",user);
if(updated>0)
    System.out.println("修改成功!"+user);
else
    System.out.println("修改失败!");

session.commit();
session.close();
System.out.println(user);

SqlSessionFactory是MyBatis的关键字,它是单个数据库映射关系经过编译后的内存镜像,SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象来获得,而SqlSessionFactoryBuildr则可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例

每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心。SqlSessionFactory是线程安全的,他一旦被创建,应该在应用执行期间都存在,在应用运行期间不要重复创建多次,建议使用单例模式,SqlSessionFactory是创建SqlSession的工厂。

11、按条件查询

按照传入的值bean的非空属性值进行等值查询,多个属性之间是条件与

<select id="selectByExample" parameterType="com.yan.entity.User" resultMap="baseMapper">
    select * from tb_users where 1=1
    <if test="id!=null">
            and id=#{id}
    </if>
    <if test="username!=null">
            and username=#{username}
    </if>
    <if test="password!=null">
            and password=#{password}
    </if>
    <if test="birth!=null">
            and birth#{birth,jdbcType=DATE}
    </if>
    <if test="sex!=null">
            and sex=#{sex,jdbcType=BOOLEAN}
    </if>
</select>

编码调用

Reader r = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);
SqlSession session = factory.openSession();

User user=new User();
user.setId(3L);
List<User> userList=session.selectList("com.yan.dao.UserMapper.selectByExample",user);

session.commit();
session.close();
userList.forEach(System.out::println);

问题:

  • 费管代码问题–工具类、继承、模板模式…
    • 必须写但跟程序功能无关
  • 编程中引用sql语句依赖的是statementId,在具体的编程中使用的是字符串类型,则无法利用IDE工具的检查功能,session.selectList("com.yan.dao.UserMapper.selectByExample",user) – 引入接口编程

MyBatis中的接口编程

给映射元文件添加一个Mapper接口 ,凡是在接口中声明的方法同时定义在接口中,具体调用时不再使用字符串的方式进行调用,而是通过接口进行调用

映射元文件

<mapper namespace="com.yan.dao.UserMapper">

这里的namespace要求对应的时具体的Mapper接口的全名

package com.yan.dao;

public interface UserMapper {
}

后面添加CRUD对应的sql语句声明时,同时向接口中添加对应的方法

CRUD是指在做计算处理时的增加(Create)、读取(Retrieve)、更新(Update)和删除(Delete)几个单词的首字母简写,CRUD主要被用在描述软件系统中数据库或者持久层的基本操作功能

<select id="selectById" parameterType="long" resultMap="baseMapper">
        select * from tb_users where id=#{id}
</select>

同时在对应的Mapper接口中添加方法,要求id和方法名称一致,parameterType参数类型和resultMap结果类型必须和方法声明一致

  • parameterType:接口中方法参数的类型, 类型的完全限定名或别名

如:parameterType = “java.lang.Integer” parameterType = “int”

这个属性是可选的,因为可以推断出具体传入语句的参数,默认值为未设置(unset)。接口中方法的参数从 java 代码传入到 mapper 文件的 sql 语句

  • MyBatis的每一个查询映射的返回类型都是ResultMap,当我们提供返回类型属性是resultType时,MyBatis会自动给我们把对应值赋给resultType所指定对象的属性,当我们提供返回类型是resultMap时,将数据库中列数据复制到对象的相应属性上,可以用于复制查询,两者不能同时用
package com.yan.dao;

import com.yan.entity.User;

public interface UserMapper {
    User selectById(Long id);
}

通过Mapper接口调用对应的selectById操作

Reader r = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(r);
SqlSession session = factory.openSession();

//接口对象是由MyBatis框架提供的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
User user=userMapper.selectById(2L);    

session.commit();
session.close();
System.out.println(user);

工具类的定义

依据:编程中涉及到的主要接口有:1、。2、 SqlSession接口来执行命令,获取映射器实例和进行管理事务。3、 用于读取封装的SqlSessionFactoryBuilder。

1、SqlSessionFactoryBuilder可以配置信息并完成SqlSessionFactory创建,一旦创建factory完毕则没有必要长期保持,所以属于即用即丢型对象

2、SqlSessionFactory主要用于完成SqlSession创建工作的。属于重量级对象,因为其中包含了二级缓存;属于线程安全的对象。属于长生命周期对象。可以在编程中考虑使用单例模式【饿汉模式、懒汉模式–双检测大的懒汉模式】

public class MyBatisSessionFactory {
    private MyBatisSessionFactory() {
    }

    private static SqlSessionFactory factory;
    static {
        try {
            buildFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getFactory() {
        return factory;
    }

    private static void buildFactory() throws Exception {
        Reader r = Resources.getResourceAsReader("mybatis-config.xml");
        factory = new SqlSessionFactoryBuilder().build(r);
    }
}

3、SqlSession充当实体管理器的功能,提供了最基本的CRUD的方法;SqlSession是Connection对象的浅封装,所以要求使用try/finally结构,属于轻量级对象,线程不安全,所以考虑使用ThreadLocal管理SqlSession对象,或者将SqlSession对象当作即用即丢型对象进行使用

浅封装:session 对connection的一个封装,只调用连接,浅浅的封装一下没有完全封装

  • Connection对象属于系统的稀有资源,因为一个数据库管理系统能够提供的连接数是有限的。要求针对Connection对象需要使用时进行创建,使用完毕必须保证及时关闭。try/finally结构
  • 在web应用中一个请求的处理流程中,不管使用了多少个类,这些类都在同一个线程上运行
private static final ThreadLocal<SqlSession> ts = new ThreadLocal<>();

public static SqlSession getSession() throws Exception {
        SqlSession session = ts.get();
        if (session == null) {
            if (factory == null)
                buildFactory();
            session = factory.openSession();
            ts.set(session);
        }
        return session;
}

public static void closeSession() throws Exception {
        SqlSession session = ts.get();
        ts.set(null);
        if(session!=null)
            session.close();
}

4、Mapper接口对象是MyBatis针对特定接口提供的代理实现对象,实际上最终还是调用映射元文件中配置的sql语句。属于线程不安全的轻量级对象,可以按照即用即丢的方式进行使用【局部变量】

在编写mybatis的程序时,常见的做法时编写一个Mapper接口,再编写相应的映射文件,之后便可以初始化mybatis的环境,调用该接口的方法执行操作数据库的各中操作。Mapper接口是使用JDK代理生成的一个代理类。

Mapper组建

  1. Mapper文件和Mapper接口应该放在同一个接口中
  2. Mapper文件中的namespace应该设置为Mapper接口的全限定名称
  3. Mapper文件中的操作元素ID对应Mapper接口的方法名称
public static <T> T getMapper(Class<T> clz) throws Exception {
        SqlSession session = getSession();
        return session.getMapper(clz);
}

5、一般在持久层框架中默认都是采用事务回滚,如果不进行手动事务提交则修改操作会在执行结束时自动回滚撤销。在工具类中添加对应的事务管理方法

public static void commitTransaction() throws Exception {
        SqlSession session = getSession();
        if (session != null)
            session.commit();
}
public static void rollbackTransaction() throws Exception{
        SqlSession session = getSession();
        if (session != null)
            session.rollback();
}

6、引入过滤器针对session和事务进行统一管理。要求进入servlet之前打开session,退出执行之前提交、回滚事务,最后还需要保证关闭session对象

@WebFilter("*.do")
public class OpenSessionInViewFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            chain.doFilter(request, response);
            MyBatisSessionFactory.commitTransaction();
        } catch (Exception e) {
            try {
                MyBatisSessionFactory.rollbackTransaction();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            throw new ServletException(e);
        } finally {
            try {
                MyBatisSessionFactory.closeSession();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值