Mybatis基础

一、介绍

  • MyBatis是一流的持久性框架
  • 支持自定义SQL,存储过程和高级映射
  • MyBatis消除了几乎所有的JDBC代码以及参数的手动设置和结果检索。MyBatis可以使用简单的XML或注释进行配置,并将图元,映射接口和Java POJO(普通的旧Java对象)映射到数据库记录。

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。)

二、第一个Mybatis程序

2.1 搭建环境

<!--导入依赖 JDBC Mybatis Junit-->
<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2.2 创建模块

  • 编写Mybatis核心配置文件
<?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>
    <properties resource="db.properties">
            <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
       
    </properties>
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
    <typeAliases>
        <typeAlias type="com.hz.pojo.User" alias="User"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="com.hz.dao"/>
    </mappers>
</configuration>

设置默认值

<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${username:ut_user}"/> <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
</dataSource>

修改默认值符号


 <property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>      
<!--修改默认值的分隔符 -->
       <dataSource type="POOLED">
  .. 
  <property name="username" value="${db:username?:ut_user}"/>
</dataSource>

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type=“[UNPOOLED|POOLED|JNDI]”):

UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

  • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息。

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

  • driver.encoding=UTF8

这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8encoding 属性给数据库驱动。

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:

  • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:

  • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
  • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:

  • env.encoding=UTF8

这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8encoding 属性。

你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory 来使用第三方数据源实现:

public interface DataSourceFactory {
  void setProperties(Properties props);
  DataSource getDataSource();
}

org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可被用作父类来构建新的数据源适配器,比如下面这段插入 C3P0 数据源所必需的代码:

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

  public C3P0DataSourceFactory() {
    this.dataSource = new ComboPooledDataSource();
  }
}

为了令其工作,记得在配置文件中为每个希望 MyBatis 调用的 setter 方法增加对应的属性。 下面是一个可以连接至 PostgreSQL 数据库的例子:

<dataSource type="org.myproject.C3P0DataSourceFactory">
  <property name="driver" value="org.postgresql.Driver"/>
  <property name="url" value="jdbc:postgresql:mydb"/>
  <property name="username" value="postgres"/>
  <property name="password" value="root"/>
</dataSource>

使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。

public interface TransactionFactory {
  default void setProperties(Properties props) { // 从 3.5.2 开始,该方法为默认方法
    // 空实现
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

2.3 编写代码User类

package com.hz.pojo;

public class User {
private int id;
private String name;
private String pwd;

  public User(){

  }

  public User(int id, String name, String pwd) {
      this.id = id;
      this.name = name;
      this.pwd = pwd;
  }

  public int getId() {
      return id;
  }

  public void setId(int id) {
      this.id = id;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public String getPwd() {
      return pwd;
  }

  public void setPwd(String pwd) {
      this.pwd = pwd;
  }

}




- Dao接口

```java
public interface UserDao {
    public List<User> getUserList();
}
  • 接口实现类由原来的UserDaoImpl转变为一个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="com.hz.dao.UserDao">
        <select id="getUserList" resultType="com.hz.pojo.User">
        select * from mybatis.User
      </select>
    </mapper>
    

三、CRUD

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z9ilAcIZ-1626338084938)(mybatis.assets/微信图片_20210617190434.png)]

1.namespace

namespace中的包名与Dao/mapper接口的类名一致

<mapper namespace="com.hz.dao.UserDao">

2.select/insert/update/delete

选择,查询语句

  • id:namespace中的方法名
  • resultType:sql语句执行的返回值
  • paramterType:参数类型
<mapper namespace="com.hz.dao.UserDao">
    <select id="getUserList" resultType="com.hz.pojo.User">
    	select * from mybatis.User
  	</select>
    <!--    对象中的属性可以直接取出-->
    <insert id="addUser" parameterType="com.hz.pojo.User">
        insert mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>
    <update id="updateUser" parameterType="com.hz.pojo.User">
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
    </update>
    <delete id="deleteUser" parameterType="com.hz.pojo.User">
        delete from mybatis.user where id=#{id};
    </delete>
</mapper>

自动生成主键

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>

3.map

  • Dao层

    User searchUser(Map<String,Object> map);
    
  • Mapper.xml

    <select id="searchUser" parameterType="com.hz.pojo.User">
            select * from user where id = #{ID} and name = #{Name};
    </select>
    

四、配置解析

1.核心配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mosBMNio-1626338084945)(mybatis.assets/image-20210317212202311.png)]

  • mapper-config.xml
  • configuaration(配置)
    • peoperties(属性)
    • setting(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManger(事务管理器)
        • dataSource(数据源)
    • databserIdProvider(数据库厂商标识)
    • mappers(映射器)

2.环境配置(environments)

Mybatis默认事务管理器:JDBC,连接池:POOLED

3.属性(properties)

两种设置属性的方式(1.建立对应peoperties文件;2.xml文件中设置peoperties)

	<properties resource="db.properties"/>
    <properties resource="temp.properties">
        <property name="name" value="name"/>
    </properties>

4.类别名(typeAliases)

	<typeAliases>
        <typeAlias type="com.hz.pojo.User" alias="User">
    </typeAliases>

5.映射器(Mapper)

三种mapper注册方式:

  • xml文件注册

    	<mappers>
            <mapper resource="com/hz/dao/UserMapper.xml"/>
        </mappers>
    
  • 接口注册

    	<mappers>
            <mapper class="com.hz.dao.UserMapper"/>
        </mappers>
    

    注意:UserMapper需要和对应xml文件在同一包中

  • 包注册

    <mappers>
            <package name="com.hz.dao"/>
        </mappers>
    

五、解决属性名和字段名不一致的问题

1.问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqJvMoHi-1626338084951)(mybatis.assets/image-20210318205140338.png)]

原因:在pojo包的User类中的属性名为password,而其对应的数据库字段名为pwd,在xml文件中的语句为select * from user与User类对象,运行时语句等同于select id,name,password from user

2.resultMap

结果集映射(如下 paword->pwd)

属性名字段名
idid
namename
passwordpwd

使用resultMap解决映射不一致问题

	<resultMap id="UserMap" type="User">
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="password"/>
    </resultMap>

六、日志

1. 日志工厂

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

2. Log4j

  • 导入Log4j的包(org.apache.log4j.Logger

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
    
  • Log4j配置文件示例

    #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=DEBUG,console,file
    
    #控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=【%c】-%m%n
    
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/kuang.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n
    
    #日志输出级别
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    

Log4j的使用

  1. 导包(org.apache.log4j.Logger

  2. 实例化logger对象

    Logger logger = Logger.getLogger(UserDaoTest.class);
    
  3. 日志等级

    logger.info("info:");
    logger.debug("debug:");
    logger.error("error:");
    

    结果如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5GYcidXF-1626338084960)(mybatis.assets/image-20210319193131873.png)]

七、分页

1. Limit分页

mysql

2.RowBound分页

  1. 接口

    List<User> getUserList();
    
  2. xml

    <select id="getUserList" resultMap="UserMap">
        select * from mybatis.User
        </select>
    
  3. Test

    import org.apache.ibatis.session.RowBounds;
    public void tese(){
    RowBounds rowBounds = new RowBounds(1,2);
    
    List<User> userList = sqlSession.selectList("com.hz.dao.UserMapper.getUserList",null,rowBounds);
    }
    

3. 使用插件

八、使用注解开发

(简单的sql语句可用注解,较复杂的用xml)

@Select("select * from user")
	List<User> getUserList();
@Select("select * from user where if = #{id}")
    User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);
@Delete("delete * from user where id = #{id} ")
    int deleteUser(User user);

九、Lombok(插件,不建议使用)

十、联表查询

表如下:

student表

idnametid
1student11
2student21
3student31

teacher表

idname
1teacher1

1、多对一

  • 方法一:select s.id,s.name,t.name from student s,teacher t where s.tid=t.id;

    <select id="getStudent" resultMap="StudentTeacher"> 
    	select s.id,s.name,t.name tname from student s,teacher t where s.tid=t.i
    </select>
    <resultMap id="StudentTeacher" type="Student">
    	<result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" javaType="Teacher">
                <result property="name" column="tname">
        </association>
    </resultMap>
    
  • 方法二:

    <!--javaType为获取的对象-->
    	<select id="getUserList" resultMap="UserMap">
        select * from User
        </select>	
    	<resultMap id="StudentTeacher" type="StudentTeacher">
            <result property="id" column="id"/>
            <result column="name" property="name"/>
            <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
        </resultMap>
        <select id="getTeacher" resultType="Teacher">
            select * from teacher where id = #{id}
        </select> 
    

2、一对多

  • 	
    <select id="getTeacher" resultMap="TeacherStudent">
            select s.id sid, s.name sname, t.name tname, t.id tid
            from student s,teacher t 
            where s.tid = t.id and t.id = #{t id};
        </select>
        <resultMap id="TeacherStudent" type="Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
            <!--
    			javatype="" 指定属性类型
    			集合中的泛型信息用ofType获取
    		-->
            <collection property="students" ofType="Student">
                <result property="id" column="sid"/>
                <result column="sname" property="name"/>
                <result property="tid" column="tid"/>
            </collection>
    

小结

javaType & ofType

  1. javaType 用来指定实体类中的属性
  2. ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型

十一、动态SQL

https://mybatis.org/mybatis-3/zh/dynamic-sql.html

搭建环境

REATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8;

创建基础工程

  1. 导包
  2. 编写配置文件
  3. 编写实体类
  4. 编写实体类对应的Mapper接口和Mapper.xml文件

if

	<select id="queryBlog" parameterType="map" resultType="Blog">
        select * from blog where title = #{title} and author = #{author}
        where 1=1
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </select>

choose、when、otherwise

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

trim、where、set

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
  <if test="state != null">
    state = #{state}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
  </where>
</select>
<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

<sql id="if-title">
	<if test="title != null">
    	title = #{title}
    </if>
    <if test="author != null">
    	and author = #{author}
    </if>
</sql>

<select ...>
	...
    <where>
    	<include refid="if-title"/>
    </where>
</select>

foreach

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
      <!-- (1,2,3,4,5) -->
  </foreach>
</select

十二、缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

1、一级缓存

SqlSession

.Post">
SELECT *
FROM POST P
WHERE ID in

#{item}


</select




# 十二、缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

```

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值