mybatis是如何将sql执行结果封装为目标对象并返回的?有哪些映射形式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis是如何将sql执行结果封装为目标对象并返回的?有哪些映射形式相关的知识,希望对你有一定的参考价值。

第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。

第二种是使用sql列的别名功能,将列别名书写为对象属性名,

有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
参考技术A 是写一个resultmap然后指明字段和属性映射关系就行 参考技术B username=#1,password=#2 where id=#0这些代码写的有问题,应该是对应的UserInfo里面的属性名,如useName,password,id等。

MyBatis学习

一、MyBatis简介

MyBatis是一个对JDBC进行封装的持久层框架,只需关注SQL本身,而不必去处理(注册驱动、创建connection、创建statement、手动设置参数、结果集检查)的代码。

XML或注解将要执行的statement配置起来,通过Java对象和statement的sql进行映射,生成最终的sql语句,由MyBatis框架执行sql并将结果映射成Java对象返回。

 

MyBatis和Hibernate的区别:

1、不完全的ORM框架,需要sql语句

2、直接写sql语句,灵活度高,性能好

3、与数据库相关(MySQL、Oracle用不同映射文件)

 

JDBC存在的问题:

1、创建连接connection、释放资源影响性能(数据库连接池可解决)

2、(sql、参数、结果集)硬编码,代码不易维护

 

二、MyBatis架构

1、SqlMapConfig.xml是核心配置文件,配置运行环境等信息;Mapper.xml是映射配置文件,配置了操作数据库的sql语句,需要在SqlMapConfig.xml中配置。

2、通过MyBatis环境等配置信息构造会话工厂SqlSessionFactory。

3、由会话工厂SqlSessionFactory创建会话sqlSession,由sqlSession操作数据库。

4、底层通过Executor执行器接口操作数据库,接口有两个实现:基本执行器、缓存执行器

5、Mapped Statement也是一个底层封装对象,包装了MyBatis配置信息和sql映射信息等。映射配置文件Mapper.xml中一个sql对应一个Mapped Statement对象,sql的id即是MappedStatement的id。

6、Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、POJO、Executor通过Mapped Statement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是JDBC中对prepareStatement设置参数

7、Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、POJO、Executor通过Mapped Statement在执行sql后将输出结果映射到Java对象中,输出结果映射过程相当于JDBC中对结果的解析处理过程。

 

三、MyBatis入门

1、导包

核心包:MyBatis-3.2.7.jar

依赖包:lib下所有包

数据库驱动包:mysql-connector-java-5.1.17-bin.jar

日志配置文件:log4j.properties

2、在src下创建核心配置文件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>
    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environmentid="development">
        <!-- 使用jdbc事务管理-->
            <transactionManagertype="JDBC"/>
        <!-- 数据库连接池-->
            <dataSourcetype="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password "value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

3、创建POJO(JavaBean)  User,属性表与表中字段对应

Public class User {
    privateintid;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址
        
    setter&getter
}

4、在src下创建映射配置文件User.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="test">
</mapper>

namespace用于隔离sql语句

5、加载映射文件User.xml到SqlMapConfig.xml

<mappers>
        <mapperresource="sqlmap/User.xml"/>
</mappers>

 

四、增删改查用户

1、根据ID查询用户信息

1.1 User.xml中

<!-- 根据id获取用户信息 -->
    <select id="findUserById" parameterType="Integer" resultType="cn.guojie.mybatis.domain.User">   // MyBatis已为Java.lang.Integer用别名Integer
        select * from user where id = #{id}
    </select>

parameterType:入参类型,通过ONGL从输入对象中获取参数值拼接到sql中

resultType:返回结果集类型,将sql查询结果的一行记录数据映射为resultType指定的类型对象

#{id}:设置占位符,并将入参id传给sql

1.2 测试

public class UserTest {
    
    //会话工厂,单例,可重复使用
    private SqlSessionFactory sqlSessionFactory;

    @Before  // 在测试方法前执行
    public void createSqlSessionFactory() throws IOException {
        // 读取核心配置文件
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);

        // 创建SqlSessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);

    }

    // 根据 id查询用户信息
    @Test
    public void testFindUserById() {
        // 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 查询单个记录,根据用户id查询用户信息,查询多条记录用selectList,命名空间+.+sql中的id
            User user = sqlSession.selectOne("test.findUserById", 10);
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

2、根据用户名查询用户信息

2.1 User.xml中

    <!-- 自定义条件查询用户列表 -->
    <select id="findUserByUsername" parameterType="java.lang.String"
            resultType="cn.guojie.mybatis.domain.User">
       select * from user where username like \'%${value}%\'  // 也可以是"%"#{id}"%"
    </selec

${}:拼接字符串,基本类型必须用value

2.2 测试

// 根据用户名称模糊查询用户信息
    @Test
    public void testFindUserByUsername() {
        // 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 模糊查询,查询用户名为张的用户
            List<User> list = sqlSession.selectList("test.findUserByUsername", "张");
            System.out.println(list.size());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

 

#{}和${}的区别:

#{}:占位符,防止SQL注入,通常在=后

${}:拼串,只能是value,将参数拼接到sql中。在页面或者action中校验,不可输入SQL关键字和空格来防止SQL注入。通常在like后

3、添加用户

3.1  User.xml中

<!-- 添加用户 -->
    <inser tid="insertUser" parameterType="cn.guojie.mybatis.domain.User">
      insert into user(username,birthday,sex,address) 
      values(#{username},#{birthday},#{sex},#{address})
    </insert>

3.2 测试

// 添加用户信息
    @Test
    public void testInsert() {
        // 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 添加用户信息
            User user = new User();
            user.setUsername("张小明");
            user.setAddress("河南郑州");
            user.setSex("1");
            user.setPrice(1999.9f);
            sqlSession.insert("test.insertUser", user);
            //提交事务,需要手动提交事务
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

4、MySQL自增主键返回

用来查询上一个插入的ID

<insert id="insertUser" parameterType="cn.guojie.mybatis.domain.User">
   <!-- selectKey将主键返回,需要再返回 -->
  <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
        select LAST_INSERT_ID()
   </selectKey>
   insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address});
</insert>

LAST_INSERT_ID():数据库海曙,返回自增的主键

keyProperty: 将返回的主键放入入参id中保存

order:相对于insert语句的执行顺序

resultType:id的类型,也就是keyproperties中属性的类型

5、MySQL中用UUID实现主键

<insert id="insertUser" parameterType="cn.guojie.mybatis.domain.User">
  <selectKey resultType="java.lang.String"  order="BEFORE" 
  keyProperty="id">
  select uuid()
  </selectKey>
  insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>

6、删除用户

6.1 User.xml中

<!-- 删除用户 -->
    <delete id="deleteUserById" parameterType="int">
        delete from user where id=#{id}
    </delete>

6.2 测试

// 根据id删除用户
    @Test
    public void testDelete() {
        // 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 删除用户
            sqlSession.delete("test.deleteUserById",18);
            // 提交事务
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

7、修改用户

7.1 User.xml

<!-- 更新用户 -->
<update id="updateUser" parameterType="cn.guojie.mybatis.domain.User">
     update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
        where id=#{id}
</update>

7.2 测试

    @Test
    public void testUpdate() {
        // 数据库会话实例
        SqlSession sqlSession = null;
        try {
            // 创建数据库会话实例sqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 添加用户信息
            User user = new User();
            user.setId(16);
            user.setUsername("张小明");
            user.setAddress("河南郑州");
            user.setSex("1");
            user.setPrice(1999.9f);
            sqlSession.update("test.updateUser", user);
            // 需要手动提交事务
            sqlSession.commit();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

 

SqlSession不是线程安全的,在方法体中创建与销毁

 

五、DAO开发

1、原始DAO开发(用得少)

需要DAO接口及其实现类

不足:

(1) 方法中代码重复(sqlSessionFactory创建SqlSession,调用SqlSession的数据可操作方法)

(2)硬编码(SqlSession操作方法需要指定statement的id)

2、Mapper动态代理(常用) 

只需Mapper接口(相当于DAO接口),MyBatis根据接口定义创建接口的动态代理对象

2.1 开发规范

(1)接口方法名和mapper.xml中id名相同

(2)入参类型和mapper.xml中入参parammeterType相同

(3)返回值类型和mapper.xml的返回类型resultType相同

(4)接口类全路径(包名+类名)和mapper.xml中命名空间相同

2.2  映射文件UserMapper.xml     放在mapper包下

<?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="cn.guojie.mybatis.mapper.UserMapper">  ----> 4
<!-- 根据id获取用户信息 -->
    <select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.po.User">  ----> 1 2 3
        select * from user where id = #{id}
    </select>
<!-- 自定义条件查询用户列表 -->
    <select id="findUserByUsername" parameterType="java.lang.String"
            resultType="cn.guojie.mybatis.domain.User">
       select * from user where username like \'%${value}%\' 
    </select>
<!-- 添加用户 -->
    <inser tid="insertUser"parameter Type="cn.guojie.mybatis.domain.User">
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
        select LAST_INSERT_ID() 
    </selectKey>
      insert into user(username,birthday,sex,address) 
      values(#{username},#{birthday},#{sex},#{address})
    </insert>

</mapper>

2.3 UserMapper接口  和映射文件放在同一目录

Public interface UserMapper {
    //根据用户id查询用户信息
    public User findUserById(int id) throws Exception;
    //查询用户列表
    public List<User> findUserByUsername(String username) throws Exception;
    //添加用户信息
    publicvoid insertUser(User user)throws Exception; 
}

2.4 加载UserMapper.xml 

<!-- 加载映射文件 -->
<mappers>
    <mapper resource="mapper/UserMapper.xml"/>
</mappers>

2.5 测试

Public class UserMapperTest {

    private SqlSessionFactory sqlSessionFactory;
    
    @Before
    protectedvoid setUp() throws Exception {
        // 加载mybatis配置文件
        String resource = "sqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 创建sessionFactory
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    Public void testFindUserById() throws Exception {
        // 获取session
        SqlSession session = sqlSessionFactory.openSession();
        // 获取mapper接口的代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);
        // 调用代理对象方法
        User user = userMapper.findUserById(1);
        System.out.println(user);
        // 关闭session
        session.close();
        
    }

    @Test
    public void testFindUserByUsername() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.findUserByUsername("张");
        System.out.println(list.size());

    }

  @Test
  Public void testInsertUser() throws Exception {
        //获取session
        SqlSession session = sqlSessionFactory.openSession();
        //获取mapper接口的代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);
        //要添加的数据
        User user = new User();
        user.setUsername("张三");
        user.setBirthday(new Date());
        user.setSex("1");
        user.setAddress("北京市");
        //通过mapper接口添加用户
        userMapper.insertUser(user);
        //提交
        session.commit();
        //关闭session
        session.close();
    }
}

 

六、核心配置文件SqlMapConfig.xml

1、<properties>

在src下定义db.properties,其中不能有空格

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

SqlMapConfig.xml中引用

<properties resource="db.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
</properties>

2、<typeAliases> 别名

<typeAliases>
    <!-- 单个别名定义 -->
    <typeAliasalias="user"type="cn.itcast.mybatis.po.User"/>
    <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->  // 常用
    <packagename="cn.guojie.mybatis.domain"/>
    <packagename="其它包"/>
</typeAliases>

3、<mapper>映射

resource=UserMapper.xml的类路径

class=UserMapper接口的类路径,要求mapper接口和mapper.xml同名且在同一目录中

<package class="接口 " /> 注册包下所有接口(常用),要求mapper接口和mapper.xml同名且在同一目录中

 

以上是关于mybatis是如何将sql执行结果封装为目标对象并返回的?有哪些映射形式的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

java面试之Mybatis篇

MyBatis学习

mybatis 那么垃圾 为什么有人用,除了封装一下结果集 我不知道他还干了什么。

MyBatis核心源码深度剖析SQL执行过程

Mybatis框架