mybatis入门学习

mybatis01

一 框架简介

1 框架介绍

框架:软件的半成品,我们只需要在他的基础之上,进行业务开发就可以.可以提高开发效率.

javaee的三层架构

  • web:之前使用的servlet开发,springmvc框架就可以简化操作(自动封装参数,简化转发和重定向操作等)
  • service:关键的内容就是事务控制,可以使用spring中声明式事务(在配置文件中配置一下,事务就不要再写)
  • dao:之前使用jdbc操作,操作步骤繁琐,之后可以使用mybatis框架,还有其他的框架(hibernate,spring JdbcTemplate…)

2 Mybatis介绍

持久层(dao)的orm框架

ORM:对象关系映射,让我们用操作对象的思想来操作数据库.

ORM就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。

mybatis它采用简单的XML配置 + 接口方法的形式实现对数据库的增删改查,xml中最主要的就是sql和参数及其返回值.使得让程序员只关注sql本身 .

也可以使用注解方式(会用),还可以使用xml配置+实现类的方式(没人用)

二 快速入门

1 需求

​ 查询数据库user表的一条记录,将查询结果封装到User对象中。

2 分析

将表和实体产生映射关系(配置文件)

3 步骤分析:

  1. 复制数据库表user
  2. 创建模块(web模块或者java模块),导入jar包(mysql驱动包和mybatis)
  3. 复制实体类User
  4. 创建UserDao接口,添加查询方法
  5. 编写映射文件(让User类和user表产生映射关系,包含sql语句)
  6. 复制核心配置文件(数据库环境-主要是连接池,映射文件的路径)
  7. 测试代码

4 实现步骤:

User表

create database mybatisdb;
use mybatisdb;
CREATE TABLE `user` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime default NULL COMMENT '生日',
  `sex` varchar(10) default NULL COMMENT '性别',
  `address` varchar(256) default NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2019-05-27 17:47:08','男','北京'),(42,'王小二','2019-03-02 15:09:37','女','北京金燕龙'),(43,'老李','2019-03-04 11:34:34','女','北京修正'),(45,'传智播客','2019-03-04 12:04:06','男','北京金燕龙'),(46,'王小二','2018-09-07 17:37:26','男','北京TBD'),(48,'小马宝莉','2019-03-08 11:44:00','女','北京修正');

导入jar和配置文件

mybatis的jar包在昨天资料中

User类

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    // 自己提供set和get方法
}

UserDao接口

public interface UserDao {
    User findById(int id);
}

映射文件

mybatis教程官网: https://mybatis.org/mybatis-3/zh/index.html

映射文件约束

<?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">

核心配置(复制)

<?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="mybatis_env">
        <!--
            id:自定义,只要项目中唯一即可
        -->
        <environment id="mybatis_env">
            <!--
                事务管理器:目前就写固定值JDBC
            -->
            <transactionManager type="JDBC"></transactionManager>
            <!--
                配置数据源:目前type就写固定值 POOLED,参考PooledDataSource源码 按俩下shift搜索源码
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatisdb"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>

    <!--修改映射文件路径-->
    <mappers>
        <!--通过resource属性确定配置文件的相对路径-->
        
    </mappers>
</configuration>

测试代码


5 扩展:log4j配置文件介绍

有了日志之后,可以清晰看到发送执行的sql语句等信息.

日志框架:log4j

使用步骤:

  1. 导入日志jar包
  2. 在src目录下提供名字为log4j.properties文件(日志的配置文件,复制昨天的)

三 快速入门详解

0 规范(稍后看)

  1. 接口全限定名与映射文件的namespace属性一致
  2. 接口的方法名与statement(子标签)的id属性一致
  3. 接口方法参数类型与statement(子标签)的parameterType属性一致
  4. 接口方法返回值类型与statement(子标签)的resultType属性一致
  5. 我再额外加一条:映射文件和接口放在一个包下,最好名字保持一致(建议)

1 映射文件概述

映射文件可以放在任意路径下,但是官方建议放在mapper包下,且名字叫:XxxMapper.xml.因为我们mybatis是一个dao层框架,我们就把这些放到dao包下且名字叫做UserDao.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">
<!--
    namespace:名称空间,内容就是dao接口的全限定类名,不要乱写
-->
<mapper namespace="com.itheima.dao.UserDao">
    <!--
        select:查询操作的statement标签
            id:dao中的方法名,不要乱写
            parameterType:参数类型的全限定类名(属性只能出现一次,但是可以省略不写)
            resultType:返回值的全限定类名
            标签体中编写sql
                参数若为简单类型(基本类型,string),获取参数的时候就使用#{参数名字}

    -->
    <select id="findById" parameterType="java.lang.Integer"
            resultType="com.itheima.domain.User">
        select * from user where id = #{id}
    </select>
    <!--
        insert:插入
        update:修改
        delete:删除
            1.以上三个标签可以混用,但是不建议
            2.不需要写resultType(也没有该属性),结果都是整数类型
    -->
</mapper>

2 核心文件概述

参考 : https://mybatis.org/mybatis-3/zh/index.html

写完几乎不修改

名字自定义,我推荐使用mybatis.xml; 路径也是自定义,我推荐放到src目录下

主要包含:数据库环境配置(连接池)和映射文件路径配置

<?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="mybatis_env">
        <!--
            id:环境的名字,自定义,只要项目中唯一即可
        -->
        <environment id="mybatis_env">
            <!--
                事务管理器:目前就写固定值JDBC
            -->
            <transactionManager type="JDBC"></transactionManager>
            <!--
                配置数据源:目前type就写固定值 POOLED,使用的是mybatis的内置连接池:PooledDataSource源码 按俩下shift搜索源码
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatisdb"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>

    <!--修改映射文件路径-->
    <mappers>
        <!--通过resource属性确定配置文件的相对路径 路径是:/-->
        <mapper resource="com/itheima/dao/UserDao.xml"/>
    </mappers>
</configuration>

3 API概述

Resources:getReousrceAsStream() 封装了类加载器加载文件的操作

SqlSessionFactoryBuilder:工厂的建造者,调用build方法,就可以解析核心配置文件,产生数据源,加载映射文件

SqlSessionFactory:是一个重量级的资源(创建和销毁都比较耗资源),线程安全对象,一个项目中只需要一个即可.

  • openSession() : 获取一个sqlSession对象,获取的对象需要手动控制事务

SqlSession:是一个线程不安全的对象,一个业务应该使用一个对象.用来和数据库打交道的对象

  • T getMapper(Class clazz):获取指定接口的代理对象

四 CRUD操作

  1. 核心配置文件就不需要再次编写了
  2. 在接口中编写方法
  3. 在映射文件中提供对应的statement(包含sql语句,参数类型,返回值)

1 添加操作

public interface UserDao {
    User findById(int id);
    int save(User user);
}

<!--
        insert:插入
            参数若是对象的话,sql中直接写#{类中属性名}
        update:修改
        delete:删除
            1.以上三个标签可以混用,但是不建议
            2.不需要写resultType(也没有该属性),结果都是整数类型
    -->
<insert id="save" parameterType="com.itheima.domain.User">
    insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert>

public class B_CRUDDemo {

    @Test
    public void testSave() throws IOException {
        //0.构建user对象,模拟service调用
        User user = new User();
        user.setUsername("睡觉哥");
        user.setBirthday(new Date());
        user.setSex("女");
        user.setAddress("顺义");

        //1.加载核心配置文件
        InputStream is = Resources.getResourceAsStream("mybatis.xml");

        //2.创建sqlSeessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

        //3.获取sqlSession对象,需要手动的提交事务
        SqlSession sqlSession = factory.openSession();

        //4.获取dao的代理对象
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        //5.调用dao中的方法
        int i = userDao.save(user);
        System.out.println(i);

        //6.提交事务和释放资源
        sqlSession.commit();
        sqlSession.close();
    }
}

2 抽取工具类

刚才我们的操作中,只是sqlsession调用的方法不同而已,对于加载配置文件、创建sqlSession、提交事务和释放资源都是一样的操作,我们就可以抽取一个工具类

  • 提供一个静态变量 SqlSessionFactory,在静态代码块中初始化
  • 提供一个静态方法,获取获取一个SqlSession对象
  • 提供一个静态方法,用来提交事务和释放资源,参数:sqlSession
  • 提供一个静态方法,用来回滚事务和释放资源,参数:sqlSession
package com.itheima.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 MybatisUtils {
    //* 提供一个静态变量 SqlSessionFactory,在静态代码块中初始化
    private static SqlSessionFactory factory;

    static {
        InputStream is = null;
        try {
            //加载核心配置文件
            is = Resources.getResourceAsStream("mybatis.xml");

            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                }
                is = null;
            }
        }

    }

    //* 提供一个静态方法,获取获取一个SqlSession对象
    public static SqlSession openSession(){
        return factory.openSession();
    }

    //* 提供一个静态方法,用来提交事务和释放资源,参数:sqlSession
    public static void commitAndClose(SqlSession sqlSession){
        sqlSession.commit();
        sqlSession.close();
    }

    //* 提供一个静态方法,用来回滚事务和释放资源,参数:sqlSession
    public static void rollbackAndClose(SqlSession sqlSession){
        sqlSession.rollback();
        sqlSession.close();
    }
}



3 其他操作

public interface UserDao {
    User findById(int id);
    int save(User user);
    void update(User user);
    void delete(int id);

    List<User> findAll();
}

<update id="update">
    update user set username = #{username},birthday = #{birthday},sex = #{sex},address = #{address} where id = #{id}
</update>

<delete id="delete">
    delete from user where id = #{id}
</delete>

<!--
        resultType:查询的结果集中每行记录要封装的类型
    -->
<select id="findAll" resultType="com.itheima.domain.User">
    select * from user
</select>

@Test
public void testUpdate() {
    //0.构建user对象,模拟service调用
    User user = new User();
    user.setId(49);
    user.setUsername("睡觉哥,别睡了");
    user.setBirthday(new Date());
    user.setSex("男");
    user.setAddress("北京顺义");

    //1.获取sqlSession
    SqlSession sqlSession = MybatisUtils.openSession();
    //2.获取dao的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    //3.执行dao中方法
    userDao.update(user);
    //4.提交事务
    MybatisUtils.commitAndClose(sqlSession);
}

@Test
public void test0() {
    //1.获取sqlSession
    SqlSession sqlSession = MybatisUtils.openSession();
    //2.获取dao的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    //3.执行dao中方法
    userDao.delete(49);
    //4.提交事务
    MybatisUtils.commitAndClose(sqlSession);
}

@Test
public void testFindAll() {
    //1.获取sqlSession
    SqlSession sqlSession = MybatisUtils.openSession();
    //2.获取dao的代理对象
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    //3.执行dao中方法
    List<User> list = userDao.findAll();
    for (User user : list) {
        System.out.println(user);
    }
    //4.提交事务
    MybatisUtils.commitAndClose(sqlSession);
}

五 mybatis配置

1 别名配置(可以不配置)

为了方便开发,mybatis给一些常用的类型起了别名, 例如:java.lang.Integer 就可以使用 integer 或者 int都可以,具体可以参考TypeAliasRegistry 此类.我们也可以给自己的domain中的这些类起别名.就需要在核心配置文件中配置一下

这个TypeAliases标签要放在environment标签上

    <typeAliases>
        <!--给一个类起别名,默认的别名就是类名或者类名首字母小写-->
        <!--<typeAlias type="com.itheima.domain.User"/>-->

        <!--还可以指定一个包,可以给这个包及其所有的子包下的类都起别名-->
        <package name="com.itheima.domain"/>
    </typeAliases>

2 映射文件路径配置

    <!--修改映射文件路径-->
    <mappers>
        <!--通过resource属性确定映射配置文件的相对路径 路径是:/-->
        <!--<mapper resource="com/itheima/dao/UserDao.xml"/>-->

        <!--若映射文件和接口放在同一个目录下,且名字一致的话-->
        <!--<mapper class="com.itheima.dao.UserDao"/>-->

        <!--若映射文件和接口放在同一个目录下,且名字一致的话 还可以通过包名批量配置-->
        <package name="com.itheima.dao"/>
    </mappers>

六 条件查询

1 简单查询(resultMap)

我们都已经完成了查询所有或者根据id查询

  • 若查询的结果和实体类中的属性都能对应上就是用resultType进行声明即可
  • 若查询的结果和实体类中的属性对应不上就可以使用resultMap标签和resultMap属性重新建立映射
<!--
        使用resultMap标签可以重新定义查询结果和实体类的映射关系
            id:起个名字,方便别人调用
            type:查询的每行记录最终要封装成的结果类型
    -->
<resultMap id="BaseResultMap" type="com.itheima.domain.User">
    <!--
            id标签是用来建立主键字段和属性的映射的
                column:查询出来的字段名称
                property:实体类中的属性名称
        -->
    <id column="id" property="id"/>
    <!--
            result标签使用来建立字段和属性的映射的
                column:查询出来的字段名称
                property:实体类中的属性名称
        -->
    <result column="uname" property="username"/>
    <result column="birthday" property="birthday"/>
    <result column="gender" property="sex"/>
    <result column="addr" property="address"/>
</resultMap>
<select id="find4ResultMap" parameterType="int"
        resultMap="BaseResultMap">
    SELECT id,username uname,birthday,sex gender,address addr FROM USER where id = #{id}
</select>


2 多个条件的查询

需求

​ 根据sex和username查询user表

方式1:在映射文件中使用arg或者param获取(了解)

方式2:通过注解方式传递参数(掌握) @Param(value=“给参数起个名字”),在映射文件中通过名字获取参数

方式3:通过user或者map对象封装多个参数,传递参数(掌握)

代码实现

List<User> findByUsernameAndSex1(String username,String sex);
List<User> findByUsernameAndSex2(@Param("name") String username,@Param("sex") String sex);
List<User> findByUsernameAndSex3(User user);

    <!--多条件查询-->
    <select id="findByUsernameAndSex1" resultType="user">
        <!--select * from user where username = #{arg0} and sex = #{arg1}-->
        select * from user where username = #{param1} and sex = #{param2}
    </select>
    <select id="findByUsernameAndSex2" resultType="user">
        select * from user where username = #{name} and sex = #{sex}
    </select>
    <select id="findByUsernameAndSex3" resultType="user">
        select * from user where username = #{username} and sex = #{sex}
    </select>

3 模糊查询

需求

​ 根据username模糊查询user表


方式1:在映射文件使用#{} 获取参数,参数需要在java代码中设置查询规则

方式2:在映射文件使用${} 获取参数,若参数类型为基本类型或者字符串,${}大括号中的值必须为value,规则可以mapper文件中写.

方式3:在sql中使用concat函数

    List<User> findByLike1(String name);
    List<User> findByLike2(String name);
    List<User> findByLike3(String name);

    <select id="findByLike1"  resultType="user">
        select * from user where username like #{name}
    </select>

    <select id="findByLike2"  resultType="user">
        select * from user where username like '%${value}%'
    </select>

    <select id="findByLike3"  resultType="user">
        select * from user where username like concat('%',#{name},'%')
    </select>

面试题:#{}和${}区别

  • #{}表示占位符,${}表示字符串拼接
  • #{}是生成预编译的sql,执行效率高 ,没有sql注入问题;${}是每次请求都会重新编译sql,执行效率低 ,有sql注入风险
  • 两者都可以接受简单类型的值和实体类类型的属性值,但是${}接收简单类型数据只能使用​${value}

七 获取新增记录主键

我们有时候有这种需求,向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键值。

解决方案:通过insert标签的selectKey标签操作

<!--
 order属性:设置获取主键值的sql语句在插入语句执行前还是执行后执行 mysql就用after即可
 扩展:oracel数据库中使用sequence(序列)创建的主键.先获取sequence再插入数据,所以oracle使用before
 keyProperty:返回的主键赋值给哪个属性
 resultType:说明返回值类型
-->
<insert id="save4GetPK">
    <selectKey order="AFTER" keyProperty="id" resultType="int">
        select last_insert_id();
    </selectKey>
    insert into user values (null,#{username},#{birthday},#{sex},#{address})
</insert>

int save4GetPK(User user);

测试:在保存成功之后 再次获取用户的id

//获取主键测试
public class TestGetPK {

    @Test
    public void test1(){
        User user = new User();
        user.setSex("男");
        user.setUsername("张三");
        user.setBirthday(new Date());
        user.setAddress("顺义");

        SqlSession sqlSession = MybatisUtils.openSession();
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        userDao.save4GetPK(user);
        System.out.println(user.getId());

        MybatisUtils.commitAndClose(sqlSession);
    }
}

扩展:通过insert标签的useGeneratedKeys属性操作–不通用,只支持有主键自增类型的数据库.oracle就不能使用

insert into user values (null,#{username},#{birthday},#{sex},#{address})

八 传统开发方式(了解)

提供dao层的接口和实现类

步骤:

  1. 映射文件还是之前的写法
  2. 核心配置文件也是之前写法
  3. 编写dao层接口,编写方法
  4. 编写dao接口的实现类,实现方法

public interface UserMapper {
    User findById(int id);
    List<User> findAll();
}

public class UserDaoImpl implements UserMapper {
    @Override
    public User findById(int id) {
        //获取sqlSession
        SqlSession sqlSession = MybatisUtils.openSession();

        //执行findById的statement,接受返回值
        User user = sqlSession.selectOne("cn.itcast.mapper.UserMapper.findById",id);

        //提交事务
        MybatisUtils.commitAndClose(sqlSession);

        //返回数据
        return user;
    }

    @Override
    public List<User> findAll() {
        SqlSession sqlSession = MybatisUtils.openSession();
        List<User> users = sqlSession.selectList("cn.itcast.mapper.UserMapper.findAll");
        //还有 insert,update,delete方法,开发中一般不用这种方式,所以大家若感兴趣可以自己编写下
        MybatisUtils.commitAndClose(sqlSession);
        return users;
    }
}

测试类

public class TestUserDao {
    @Test
    public void testFindById(){

        //创建UserDao对象
        UserDao userDao = new UserDaoImpl();

        //调用findById方法
        User user = userDao.findById(42);

        System.out.println(user);
    }

    @Test
    public void testFindAll(){
        UserDao userDao = new UserDaoImpl();
        List<User> list = userDao.findAll();
        if (list!=null && list.size()>0) {
            for (User user : list) {
                System.out.println(user);
            }
        }
    }
}

九 总结

DAO使用接口代理的方式进行操作

规范:

  1. 接口全限定类名和映射文件的namespace一样
  2. 接口中方法名和映射文件中statement的id一样
  3. 接口中方法参数类型和映射文件中statement的parameterType一样
  4. 接口中方法返回值类型和映射文件中statement的resultType或者resultMap中type一样
  5. 接口和映射文件名字及路径保证一样

别名配置:在核心配置文件中配置,配置了之后就可以在映射文件中直接使用domain中类的别名了

映射文件的路径配置:在核心配置文件中直接配置接口的包名(dao包)就可以了.

resultMap配置:当查询的结果和实体类中的属性对应不上的时候,就可以重新映射


条件查询:

  • 1个条件的时候,parameterType可以省略不写.若是简单类型的话,可以在sql中#{任意值};若是其他引用类型的话,可以在sql中使用#{属性名或者键名}
  • 多个条件的话,可以使用javaBean或者map封装.也可以使用@Param注解传递参数
  • 模糊查询
    • 可以使用#{}, 在java代码中设置模糊匹配的规则,底层依然使用的PreparedStatement
    • 可以使用${…},可以在映射文件中编写匹配规则.底层使用的Statement
    • 可以concat()在映射文件中拼接规则,配置#{}获取参数

获取新增记录的主键

  • 使用selectKey子标签,推荐使用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值