MyBatis_XML映射文件

4. XML - 映射文件

MyBatis 的真正强大在于它的语句映射,MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

SQL 映射文件只有很少的几个顶级元素(只列出常用元素):

  • select – 映射查询语句。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • sql – 可被其它语句引用的可重用语句块。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。

4.1. select

查询语句是 MyBatis 中最常用的元素之一,能把数据存到数据库中价值并不大,还要能重新取出来才有用,多数应用也都是查询比修改要频繁。前面我们写了一个比较简单的select

<select id="selectOne" parameterType="int" resultType="com.czxy.mybatis.model.User">
    SELECT
    uid,
    username,
    birthday,
    phone,
    sex,
    address
    FROM `user`
    WHERE uid = #{aa}
</select>

这个语句名为selectOne,接受一个 int(或 Integer)类型的参数,并返回一个 User类型的对象.

    @Test
    public void selectOne()throws IOException {
        //配置文件位置
        String resource = "mybatis-config.xml";
        //读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过配置文件创建SessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
        //可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
        //读取数据库连接
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //通过 namespace.id的方式确定要执行的SQL片段
        User user = sqlSession.selectOne("test.selectOne",12);
        System.out.println(user);
        //用完后必须管理连接
        sqlSession.close();
    }
4.1.1. 参数符号

注意参数符号:#{aa}

这就告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,

这样做更安全,更迅速,通常也是首选做法,因为参数会被双引号 “” 包裹,避免SQL注入,上述语句对应的SQL:

#<!-- #{id}表示占位符:获取传递的参数    ${id}原封不动的拼接-->
   
SELECT
    uid,
    username,
    birthday,
    phone,
    sex,
    address
FROM `user`
WHERE uid = "10"

参数符号还有另外一种方式: ${id}

有时你就是想直接在 SQL 语句中直接插入一个不转义的字符串。 比如 ORDER BY 子句,这时候你可以:

ORDER BY ${columnName}
-- mapper
SELECT 
  uid,
  username,
  birthday,
  phone,
  sex,
  address
FROM `user`
ORDER BY ${columnName} DESC

-- 对应的SQL
SELECT 
  uid,
  username,
  birthday,
  phone,
  sex,
  address
FROM `user`
ORDER BY uid DESC -- 这里的列名就没有被双引号包裹
4.1.2. 参数类型

parameterType 用于明确参数类型,可以指定这条语句的参数类的全限定名或别名。

这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)

  • 参数只有一个
    • int / java.lang.Integer
    • string
    • float
    • double
    • date
  • 参数有多个
    • parameterType=“map” / java.util.HashMap
    • user / com.czxy.mybatis.model.User
  • 使用map和pojo类型传参

UserMapper.xml

<!--参数是map类型  需求:根据手机号或者性别进行查询-->
<select id="listUser" parameterType="map" resultType="user">
    SELECT
    uid,
    username,
    birthday,
    phone,
    sex,
    address
    FROM `user`
    WHERE sex = #{sex}
    AND phone like #{phone}
</select>

MyBatisTest类

@Test
public void listUser() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //通过sqlSession获取与数据库的会话连接,完成各种SQL的执行
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //准备查询参数
    //1. pojo类型
    //User user = new User();
    //user.setPhone("%"+888+"%");
    //user.setSex("1");

    //2.map类型参数,注意key必须和占位符名称一致#{key},否则获取不到参数
    HashMap<String, Object> map = new HashMap<>();
    map.put("phone","%"+888+"%");
    map.put("sex","1");

    //通过 namespace.id的方式确定要执行的SQL片段
    //List<User> list = sqlSession.selectList("test.listUser", user);
    List<User> list = sqlSession.selectList("test.listUser", map);

    for (User u : list) {
        System.out.println(u);
    }
    //用完后必须管理连接
    sqlSession.close();
}
4.1.3. 结果映射
resultType
  1. 返回结果的数据类型。通常 MyBatis 可以推断出来,但是为了更加准确,建议写上。
  2. 返回一条或者多条数据,结果类型不变
    • 返回一条User类型数据和返回多条User类型数据,resultType结果都是resultType=“user”
  3. resultType和resultMap必须有一个属性被指定
<select id="selectUser" parameterType="int" resultType="map">
    SELECT
    uid,
    username,
    birthday,
    phone,
    sex,
    address
    FROM `user`
    WHERE uid = #{uid}
</select>

返回结果:

{birthday=1998-10-16, uid=10, address=北京海淀, phone=13999999999, sex=1, username=张三}
- resultMap

当查询结果有多个列,并且列名和字段名称不一致时,mybatis无法将结果封装到model对象中,这是需要通过resultMap绑定列和属性关系

<!--resultMap结果映射:绑定列和属性关系-->
<resultMap id="baseResultMap" type="user">
    <id property="uid" column="uid" javaType="int" jdbcType="INTEGER"></id>
    <result property="username" column="username"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <result property="phone" column="phone"></result>
    <result property="address" column="address"></result>
</resultMap>

<!--参数是map类型, 返回结果类型是resultMap-->
<select id="listUser2" parameterType="map" resultMap="baseResultMap">
    SELECT
    uid,
    username,
    birthday,
    phone,
    sex,
    address
    FROM `user`
    WHERE sex = #{sex}
    AND phone like #{phone}
</select>

测试:MyBatisTest.java

@Test
public void listUser2() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //通过sqlSession获取与数据库的会话连接,完成各种SQL的执行
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //map类型参数,注意key必须和占位符名称一致#{key},否则获取不到参数
    HashMap<String, Object> map = new HashMap<>();
    map.put("phone","%"+6+"%");
    map.put("sex","1");

    //通过 namespace.id的方式确定要执行的SQL片段
    List<User> list = sqlSession.selectList("test.listUser2", map);

    for (User u : list) {
        System.out.println(u);
    }
    //用完后必须管理连接
    sqlSession.close();
}
附件 - 支持的 JDBC 类型

为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。

BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
TINYINT REAL VARCHAR BINARY BLOB NVARCHAR
SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR
INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
BIGINT DECIMAL TIME NULL CURSOR ARRAY

- 内容回顾

  1. mybatis 入门案例

    1. 导入maven依赖
    2. 添加了配置文件
      1. jdbc.properties : 配置了数据库的连接
      2. mybatis-config.xml : 整合jdbc.properties / UserMapper.xml ,这样mybatis是不是清楚了数据源在哪,清楚要执行的具体SQL有哪些
      3. UserMapper.xml : SQL片段(statement)
    3. 测试程序:读取了核心配置文件mybatis-config.xml,创建了sqlSessionFactory,通过sqlSessionFactory可以创建sqlSession(会话连接)连接到数据库,可以使用sqlSession执行SQL片段
  2. 入门程序中一些细节

    1. mybatis-config.xml
    2. UserMapper.xml
      1. 主要编写SQL片段
      2. 参数
        1. 指定接收参数类型:resultType=“别名”,默认可以不写,mybatis会自动推导
        2. 使用接收参数:#{} ${} ,
          1. #{}使用占位符 ? ,参数被“”包裹,好处-安全
          2. ${}: 直接把参数拼接到SQL后面,在 group by uid
      3. 返回值类型
        1. map
        2. 使用pojo对象接收:类的属性和表的字段名称要么一模一样,要么符合驼峰命名规则
          1. 属性名: userName 对应字段 user_name
        3. 特殊情况:不符第二种规则,需要手动绑定字段和属性的关系
          1. 使用resultMap 手动绑定column和属性之间的关系

4.2. insert (插入数据)

  • UserMapper.xml
<!--parameterType可以省略,mybatis会自动推导参数类型-->
<insert id="insertUser">
    INSERT INTO `user`
    (uid,username,birthday,phone,sex,address)
    VALUES(NULL,#{username},#{birthday},#{phone},#{sex},#{address})
</insert>
  • 测试:MyBatisTest.java
//插入数据
@Test
public void testInsert() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //通过sqlSession获取与数据库的会话连接,完成各种SQL的执行
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //准备数据
    User user= new User();
    user.setUsername("曹操");
    user.setBirthday(new Date());
    user.setAddress("安徽亳州");
    user.setPhone("13666666666");
    user.setSex("1");

    //通过 namespace.id的方式确定要执行的SQL片段
    sqlSession.insert("test.insertUser",user);

    //提交事务
    sqlSession.commit();
    //用完后必须管理连接
    sqlSession.close();
}
4.2.1. 返回生成的主键ID

在插入语句里面有一些额外的属性和子元素用来处理主键的生成,如果你的数据库支持自动生成主键的字段,那么可以进行如下设置 :

  • useGeneratedKeys=”true”,
  • keyProperty =“目标属性”

这样做的好处是可以返回自动生成的主键。

  • 应用场景:商品相关操作的日志记录

管理员新上架商品:张三(10)上架华为P40 Pro(1001),对应日志如下:
在这里插入图片描述
user表已经在 uid 列上使用了自增主键,那么语句可以修改为:

  • UserMapper.xml
 <!--1. 先观确定主键ID是否是自增的-->
 <!--2. 修改成能返回主键ID-->
<!--插入user数据,并且返回主键ID-->
<!-- - useGeneratedKeys=true” 使用生成的主键,默认是false,不适用生成的主键(不返回生成的主键),-->
<!-- - keyProperty ="目标属性" 把主键返回到哪个属性中-->
<insert id="insertUserAndReturnID" useGeneratedKeys="true" keyProperty="uid">
    INSERT INTO `user`
    (uid,username,birthday,phone,sex,address)
    VALUES(NULL,#{username},#{birthday},#{phone},#{sex},#{address})
</insert>
  • 测试:MyBatisTest.java
  @Test
public void testInsert() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //通过sqlSession获取与数据库的会话连接,完成各种SQL的执行
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //准备数据
    User user= new User();
    user.setUsername("曹操");
    user.setBirthday(new Date());
    user.setAddress("安徽亳州");
    user.setPhone("13666666666");
    user.setSex("1");

    //通过 namespace.id的方式确定要执行的SQL片段
    //sqlSession.insert("test.insertUser",user);
    sqlSession.insert("test.insertUserAndReturnID",user);
    System.out.println("---------- " +user.getUid()+" -----------");
    //提交事务
    sqlSession.commit();
    //用完后必须管理连接
    sqlSession.close();
}

4.3. update(更新数据)

UserMapper.xml

<update id="updateUser" >
    UPDATE `user`
    SET phone = #{phone}
    WHERE uid = #{uid}
</update>
  • 测试:MyBatisTest.java
@Test
public void updateUser() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //通过sqlSession获取与数据库的会话连接,完成各种SQL的执行
    SqlSession sqlSession = sqlSessionFactory.openSession();

    User user = new User();
    user.setUid(32);
    user.setPhone("13612345678");
    //通过 namespace.id的方式确定要执行的SQL片段
    sqlSession.update("test.updateUser",user);

    //提交事务
    sqlSession.commit();
    //用完后必须管理连接
    sqlSession.close();
}

4.4. delete(删除数据)

  • UserMapper.xml
<delete id="deleteUser" >
    DELETE FROM `user` WHERE uid = #{uid}
</delete>
  • 测试:MyBatisTest.java
@Test
public void deleteUser() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //通过sqlSession获取与数据库的会话连接,完成各种SQL的执行
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //使用map传递参数,KEY必须和占位符#{key}名称一致,否则获取不到参数
    HashMap<String, Integer> map = new HashMap<>();
    map.put("uid",2);
    //通过 namespace.id的方式确定要执行的SQL片段
    sqlSession.delete("test.deleteUser",map);

    //提交事务
    sqlSession.commit();
    //用完后必须管理连接
    sqlSession.close();
}

4.5. SQL

  • SQL元素的作用

前面我们在写SQL的时候可以发现,出现大量重复的SQL片段,例如查询的时候,列名在个SQL都有书写

SELECT
    uid,
    username,
    birthday,
    phone,
    sex,
    address
FROM `user`
WHERE uid = #{aa}
  • 使用SQL元素
    • 使用SQL元素定义SQL片段
    • 使用<include refid="片段ID"></include>引用
  • 示例:这里用SQL元素来定义可重用的 SQL 代码片段,以便在其它语句中使用。
<sql id="baseColunm">
    uid,username,birthday,phone,sex,address
</sql>

<!--参数是map类型, 返回结果类型是resultMap-->
<select id="listUser2" parameterType="map" resultMap="baseResultMap">
    SELECT
    	<include refid="baseColunm"></include>
    FROM `user`
    WHERE sex = #{sex}
    AND phone like #{phone}
</select>

4.6. 添工具类

创建sqlSessionFactory和获取sqlSession的方式是固定的,sqlSessionFactory只需要创建一次即可,因此使用工具类MyBatisUtils来封装相关操作,简化书写,后续SSM整合之后,这些对象的创建就交给spring容器管理了,不需要我们自己管理了。

public class MyBatisUtils {

    private static SqlSessionFactory sqlSessionFactory;
    /*
     * 创建本地线程变量,为每一个线程独立管理一个session对象 每一个线程只有且仅有单独且唯一的一个session对象
     * 使用ThreadLocal对session进行管理,可以保证线程安全,避免多实例同时调用同一个session对象
     */
    private static ThreadLocal<SqlSession> threadlocal = new ThreadLocal<SqlSession>();

    // 创建sessionFactory对象,因为整个应用程序只需要一个实例对象,故用静态代码块
    static {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 新建session会话,并把session放在线程变量中
     */
    private static void newSession() {
        // 打开一个session会话
        SqlSession session = sqlSessionFactory.openSession();
        SqlSession sqlSession = threadlocal.get();
        // 将session会话保存在本线程变量中
        threadlocal.set(session);
    }

    /**
     * 返回session对象
     * @return session
     */
    public static SqlSession getSession(){
        //优先从线程变量中取session对象
        SqlSession session = threadlocal.get();
        //如果线程变量中的session为null,
        if(session==null){
            //新建session会话,并把session放在线程变量中
            newSession();
            //再次从线程变量中取session对象
            session = threadlocal.get();
        }
        return session;
    }

    /**
     * 关闭session对象,并从线程变量中删除
     */
    public static void closeSession(){
        //读取出线程变量中session对象
        SqlSession session = threadlocal.get();
        //如果session对象不为空,关闭sessoin对象,并清空线程变量
        if(session!=null){
            //关闭资源
            session.close();
            //从threadlocal中移除session
            threadlocal.set(null);
        }
    }

    /**
     * 提交并关闭资源
     */
    public static void commitAndclose() {

        //获取连接
        SqlSession openSession = getSession();
        //非空判断
        if(openSession!=null) {
            //提交
            openSession.commit();
            //关闭资源
            openSession.close();
            //从threadlocal中移除session
            threadlocal.remove();
        }
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44509920/article/details/107556306
今日推荐