avatar

目录
Mybatis笔记(一)

参考:【itheima】

[toc]

mybatis概述

jdbc问题总结

  • 没有数据库连接池,浪费资源
  • sql硬编码,无法维护

mybatis概述

  1. mybatis配置

    • SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
    • mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
  2. 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂

  3. 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。

  4. mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。

  5. Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。

  6. Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。

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

入门程序

搭建

  • 下载:https://github.com/mybatis/mybatis-3/releases

  • 文件结构

    • mybatis-3.2.7.jar—-mybatis的核心包
    • lib—-mybatis的依赖包
    • mybatis-3.2.7.pdf—-mybatis使用手册
  • jar包:

    • mybatis核心包,依赖包,数据驱动包
  • log4j.properties

    • 在classpath下创建log4j.properties如下:

      Code
      1
      2
      3
      4
      5
      6
      # Global logging configuration
      log4j.rootLogger=DEBUG, stdout
      # Console output...
      log4j.appender.stdout=org.apache.log4j.ConsoleAppender
      log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
      log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    • mybatis默认使用log4j作为输出日志信息。

  • SqlMapConfig.xml

    xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?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="development">
    <environment id="development">
    <transactionManager type="JDBC" />

    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://10.211.55.6:3306/mybatis?characterEncoding=utf-8" />
    <property name="username" value="machine" />
    <property name="password" value="4869" />

    </dataSource>
    </environment>
    </environments>
    </configuration>
  • PO类

    • Po类作为 mybatis进行sql映射使用,po类通常与数据库表对应

      Code
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class User {

      private int id;
      private String username;
      private String sex;
      private Date birthday;
      private String address;

      //set,get...
      }
  • sql映射文件

    • 在classpath下的sqlmap目录下创建sql映射文件Users.xml

      Code
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
              <?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">
      <!--
      namespace: 用来隔离sql语句
      例如:User user = sqlSession.selectOne("test.findUserById", 10);
      -->
      </mapper>
      ```

      - 加载映射文件
      - 添加在SqlMapConfig.xml下
      Code
      1
      2
      3
      4
         
      ## 根据id查询用户信息

      - user.xml,在mapper下
      Code
      1
      2


      //查询多例 List list = sqlSession.selectList("test.findUserByName", "t");

System.out.println(list);

Code
1
2

## 添加用户
INSERT INTO User(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
Code
1
2


User user = new User(); user.setUsername("xiaoming"); user.setBirthday(new Date()); user.setSex("女"); user.setAddress("海南");

sqlSession.selectOne(“test.insertUser”,user);

//提交事务
sqlSession.commit();

//…

Code
1
2

## 删除用户
DELETE FROM User WHERE id = #{id}
Code
1
2


sqlSession.delete("test.deleteUserById",11); sqlSession.commit();
Code
1
2

## 修改用户
UPDATE User SET username = #{username}, sex = #{sex}, birthday = #{birthday}, address = #{address} WHERE id = #{id}
Code
1
2


User user = new User(); user.setId(10); user.setUsername("wq"); user.setSex("男"); user.setAddress("四川"); user.setBirthday(new Date());

//若有的字段不想更新,必须先查询先前的值,再注入进去,不然(不写的话),那个字段的值会变空
sqlSession.update(“test.updateUser”,user);

sqlSession.commit();

Code
1
2
3
4
5
6
7
8
9
10
11
12

# 小结 对比
## ${} 和 #{}

- ‘#{}’ 实现preparedStatement向**占位符中**设置值,自动进行java类型和jdbc类型转换,#{}括号中可以是value或其它名称。
- ‘${}’ 只是**拼接**sql串,不进行jdbc类型转换,括号中只能是value。

## selectOne和selectList
- 动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。


## mysql自增主键返回
SELECT LAST_INSERT_ID()
INSERT INTO User(username,birthday,sex,address)
VALUES(#{username},#{birthday},#{sex},#{address})
Code
1
2


User user = new User();

user.setUsername(“maxiao”);
user.setBirthday(new Date());
user.setSex(“女”);
user.setAddress(“合肥”);

System.out.println(“=====”+user.getId());

sqlSession.insert(“test.insertUser”,user);
//提交事务
sqlSession.commit();

System.out.println(“=====”+user.getId());

Code
1
2
3
4
5

## mysql使用uuid实现主键

- select uuid()生成主键,再注入po,再把po插入数据库
- 修改User 的 id 字段为String类型
<insert id="insertUser" parameterType="com.machine.po.User" >
    <!--before: 将uuid的值注入po,再插入数据库-->
    <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
        select uuid()
    </selectKey>

    INSERT INTO User(id,username,birthday,sex,address)
    VALUES(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

## Mybatis解决jdbc编程的问题
- 有数据库链接池
- sql与java代码分离
- 自动将java对象映射至sql语句,parameterType
- 自动将sql执行结果映射至java对象,resultType

## mybatis与hibernate不同
- Mysql需自己编写sql语句,不完全是一个ORM框架
- 原生态sql,灵活度高,适合对关系数据模型要求不高的软件开发(互联网软件、企业运营类软件)
- 无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大
- Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。
- Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
- 所以框架只有适合才是最好


## SqlSession的使用范围
- SqlSessionFactoryBuilder
- SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围即**方法体内局部变量**。
- SqlSessionFactory
- SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以**单例**模式管理SqlSessionFactory。
- SqlSession
- SqlSession是一个面向用户的接口, sqlSession中定义了数据库操作方法。
- 每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
- 打开一个 SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。

# DAO开发方法
## 原始Dao开发方法(不推荐)

- 程序员编写Dao接口和Dao实现类
public interface UserDao { public User getUserById(int id); public void InsertUser(User user); }
Code
1
 
public class UserDaoImpl implements UserDao{ //注入sqlSessionFactory private SqlSessionFactory sqlSessionFactory; public UserDaoImpl(SqlSessionFactory sqlSessionFactory){ this.sqlSessionFactory = sqlSessionFactory; } @Override public User getUserById(int id) { SqlSession sqlSession = sqlSessionFactory.openSession(); User user = null; try { user = sqlSession.selectOne("test.findUserById",id); }finally { sqlSession.close(); } return user; } @Override public void InsertUser(User user) { SqlSession sqlSession = sqlSessionFactory.openSession(); try{ sqlSession.insert("test.insertUser",user); sqlSession.commit(); }finally { sqlSession.close(); } } }
Code
1
 
@Test public void testDao(){ UserDao userDao = new UserDaoImpl(sqlsessionFactory); User user = userDao.getUserById(1); System.out.println(user); User user2 = new User(); user2.setUsername("er"); user2.setSex("女"); user2.setBirthday(new Date()); user2.setAddress("西雅图"); userDao.InsertUser(user2); }
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   
- 原始dao开发存在以下问题
- 代码重复:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
- 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不利于开发维护

## Mapper接口开发方法(推荐)

- Mapper动态代理方式
- 开发规范
- 程序员只需编写Mapper接口(相当于Dao接口),由Mybatis框架 根据接口定义 创建接口的**动态代理**对象,代理对象的方法体同上边Dao接口实现类方法。
- Mapper接口开发需要遵循以下规范:
1. Mapper.xml文件中的namespace与mapper接口的类路径相同。
2. Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3. Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4. Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
- Mapper.xml(映射文件)
- 定义mapper映射文件UserMapper.xml(内容同Users.xml),需要修改namespace的值为 UserMapper接口路径。将UserMapper.xml放在classpath 下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"> <!--namespace: mapper接口路径--> <mapper namespace="com.machine.mapper.UserMapper"> <select id="findUserById" parameterType="int" resultType="com.machine.po.User"> SELECT * FROM User where id = #{id} </select> <select id="findUserByName" parameterType="java.lang.String" resultType="com.machine.po.User"> SELECT * FROM User where username LIKE '%${value}%' </select> <insert id="insertUser" parameterType="com.machine.po.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>
Code
1
2
3


- XxxMapper.java(接口文件)
public interface UserMapper { public User findUserById(int id) throws Exception; public List<User> findUserByName(String username) throws Exception; public void insertUser(User user) throws Exception; }
Code
1
2
 
- 加载UserMapper.xml文件(核心文件中)
<mapper resource="mapper/UserMapper.xml" />
Code
1
2
   
- 测试代码
@Test public void testMapper1() throws Exception { SqlSession sqlSession = sqlsessionFactory.openSession(); //获取mapper接口的 代理对象 , // sqlSession.getMapper()返回的是对象 ,用接口接受 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); System.out.println("=====按id查找"); User user = userMapper.findUserById(2); System.out.println(user); System.out.println("=====按name查找"); List<User> users = userMapper.findUserByName("m"); System.out.println(users); System.out.println("=====添加"); User user2 = new User(); user2.setUsername("tt"); user2.setAddress("马达加斯加"); user2.setBirthday(new Date()); user2.setSex("男"); userMapper.insertUser(user2); //提交事务,关闭session sqlSession.commit(); sqlSession.close(); }
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   
- mybatis官方推荐使用mapper代理方法开发mapper接口


# SqlMapConfig.xml配置文件
## 配置内容
- 顺序如下:

```t
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)

properties(属性)

  • 在classpath下定义db.properties文件

    Code
    1
    2
    3
    4
    5
    6
    7
        jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://10.211.55.6:3306/mybatis?characterEncoding=utf-8
    jdbc.username=machine
    jdbc.password=4869
    ```

    - SqlMapConfig.xml引用如下:
        <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>
    Code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
       
    - MyBatis 将按照下面的顺序来加载属性:
    - 在 properties 元素体内定义的属性首先被读取
    - 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性

    ## typeAliases(类型别名)

    - mybatis支持别名

    ```t
    别名 映射的类型
    _byte byte
    _long long
    _short short
    _int int
    _integer int
    _double double
    _float float
    _boolean boolean
    string String
    byte Byte
    long Long
    short Short
    int Integer
    integer Integer
    double Double
    float Float
    boolean Boolean
    date Date
    decimal BigDecimal
    bigdecimal BigDecimal
    map Map
  • 自定义别名

    • 在SqlMapConfig.xml中配置:

      Code
      1
      2
      3
      4
      5
      6
      7
      8
      9
      <typeAliases>
      <!--单个定义别名-->
      <typeAlias alias="user" type="com.machine.po.User" />
      <!--
      使用扫描法 批量定义别名
      定义后,别名=类名,不区分大小写,单建议按java命名规则(userMapper)
      -->
      <package name="com.machine.po" />
      </typeAliases>

mappers(映射器)

  • <mapper resource=" " />

    • 使用相对于类路径的资源,
      • 如:<mapper resource="sqlmap/User.xml" />
  • <mapper class=" " />

    • 使用mapper接口类路径,
      • 如: <mapper class="com.machine.mapper.UserMapper"/>
    • 注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
  • <package name=""/>

    • 注册指定包下的所有mapper接口,
      • 如:<package name="com.machine.mapper"/>
    • 注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中
文章作者: Machine
文章链接: https://machine4869.gitee.io/2018/04/20/15326649627690/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 哑舍
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论