MyBatis学习笔记

Mybaits学习笔记

入门

  1. 创建maven工程并导入坐标
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
  1. 创建实体类和dao接口

    User.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int ind;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

​ IUserDao.java

public interface IUserDao {
   	//查询所有
    List<User> findAll();
}
  1. 创建Mybaits主配置文件

    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>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务的类型-->
            <transactionManager type="JDBC"/>
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!--配置链接数据库的四个信息-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/jdbcdb?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

<!--映射配置文件-->
    <mappers>
        <mapper resource="com/hzj/dao/IUserDao.xml"/>
    </mappers>

</configuration>
  1. 创建映射配置文件
<?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.hzj.dao.IUserDao">
    <!--配置查询所有-->
    <select id="findAll" resultType="com.hzj.entity.User">
        select * from user
    </select>
</mapper>
  • mybatis的映射配置文件位置必须和dao接口的包结构相同
  • 映射配置文件的 mapper 标签 namespace 属性必须是dao接口的全限定类名
  • 映射配置文件的操作配置 <select> ,id属性的取值必须是dao接口的方法名
  • 映射配置文件的 resultType 设置返回值的类型(告诉Mybaits要封装到哪里去)

入门案例

public class MyBatisTest {
    public static void main(String[] args) throws IOException {
        //1.读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工厂生产SqlSession对象
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建Dao接口的代理对象
        IUserDao dao = session.getMapper(IUserDao.class);
        //5.使用代理对象执行方法
        List<User> list = dao.findAll();
        for (User user : list) {
            System.out.println(user);
        }
        //6.释放资源
        session.close();
        in.close();
    }
}

注解配置入门

IUserDao.java 中添加注解

public interface IUserDao {
    //查询所有
    @Select("select * from user")
    List<User> findAll();
}

SqlMapConfig.xml 中使用class属性指定被注解的dao全限定类名

<mappers>
    <mapper class="com.hzj.dao.IUserDao"/>
</mappers>

mybatis增删改查

执行添加、修改和删除需要提交事务

@After
public void destroy() throws IOException {
    //提交事务
    session.commit();
    //释放资源
    session.close();
    in.close();
}

添加

IUserDao.java

void save(User user);

IUserDao.xml

<insert id="save" parameterType="com.hzj.entity.User">
    insert into user (username, sex)
    values (#{username}, #{sex});
</insert>

测试方法 UserDaoTest.java

public void testSave() {
    User user = new User();
    user.setUsername("test mybatis save");
    user.setSex("男");
    //执行保存方法
    dao.save(user);
}

更新

IUserDao.java

void update(User user);

IUserDao.xml

<update id="update" parameterType="com.hzj.entity.User">
    update user
    set username = #{username},
    sex      = #{sex}
    where id = #{id};
</update>

测试方法 UserDaoTest.java

public void testUpdate(){
    User user = new User();
    user.setUsername("test mybatis update");
    user.setSex("男");
    user.setId(62);
    //执行更新方法
    dao.update(user);
}

删除

IUserDao.java

void delete(int id);

IUserDao.xml

参数类型只有一个基本类型时,#{}里随便写,表示占位符

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

测试方法 UserDaoTest.java

public void testDelete(){
    dao.delete(63);
}

根据id查询

IUserDao.java

User findById(int id);

IUserDao.xml

resultType表示返回值类型

<select id="findById" parameterType="int" resultType="com.hzj.entity.User">
    select * from user where id = #{id};
</select>

测试方法 UserDaoTest.java

public void testFindById(){
    User user = dao.findById(46);
    System.out.println(user);
}

模糊查询

IUserDao.java

List<User> findByName(String username);

IUserDao.xml

<select id="findByName" parameterType="String" resultType="com.hzj.entity.User">
    select * from user where username like #{username};
</select>

测试方法 UserDaoTest.java

public void testFindByName(){
    //需要加百分号
    List<User> list = dao.findByName("%王%");
    for (User user : list) {
        System.out.println(user);
    }
}

另一种方式(不常用) Statement的字符串拼接 有安全问题

UserDao.xml

<select id="findByName" parameterType="String" resultType="com.hzj.entity.User">
    select * from user where username like '%${value}%';
</select>

测试方法 UserDaoTest.java

public void testFindByName(){
    //不需要加百分号
    List<User> list = dao.findByName("王");
}

获取新增存数据的id

IUserDao.xml

<insert id="save" parameterType="com.hzj.entity.User">
    -- keyProperty:实体类属性 keyColumn:数据库列名
    -- order:插入后的行为 resultType:返回值类型
    <selectKey keyProperty="id" keyColumn="id" order="AFTER" resultType="int">
        select last_insert_id();
    </selectKey>
    insert into user (username, sex)values (#{username}, #{sex});
</insert>

测试方法 UserDaoTest.java

public void testSave() {
    User user = new User();
    user.setUsername("test mybatis save");
    user.setSex("男");

    System.out.println("保存操作之前:" + user);
    //执行保存方法
    dao.save(user);
    System.out.println("保存操作之后:" + user);
}

输出结果

保存操作之前:User(id=0, username=test mybatis save, sex=男)
保存操作之后:User(id=64, username=test mybatis save, sex=男)

使用实体类的包装对象作为查询条件

  • 由多个对象组成一个对象作为查询条件

QueryVo.java 查询条件对象

@Data
@AllArgsConstructor
@NoArgsConstructor
public class QueryVo {
    private User user;
}

IUserDao.java

List<User> findByVo(QueryVo vo);

IUserDao.xml

<select id="findByVo" parameterType="com.hzj.entity.QueryVo" resultType="com.hzj.entity.User">
    select * from user where username like #{user.username};
</select>

测试方法 UserDaoTest.java

public void testFindByVo(){
    User user = new User();
    user.setUsername("%王%");
    List<User> list = dao.findByVo(new QueryVo(user));
    for (User u : list) {
        System.out.println(u);
    }
}

mybatis的参数

如果实体类属性和数据库列名不对应

User.java改成这样

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int userId;
    private String userName;
    private String userSex;
}

而数据库列名分别为 id username sex

在IUserDao.xml中配置查询结果列名和实体类属性的对应关系

<resultMap id="userMap" type="com.hzj.entity.User">
    <!--主键字段对应-->
    <id property="userId" column="id"/>
    <!--非主键字段对应-->
    <result property="userName" column="username"/>
    <result property="userSex" column="sex"/>
</resultMap>

在后面的sql语句的属性中 resultType 改为 resultMap 并指定为 resultMap 的id

配置文件的其他

properties标签的使用及细节

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>

    <properties>
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>

    <environments default="mysql">
        <environment id="mysql">
            <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>
        <mapper resource="com/hzj/dao/IUserDao.xml"/>
    </mappers>

</configuration>
  • 外部引用的方式

resource(常用):用于执行配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下

url:要求按照URL的写法来写地址

<properties resource="JdbcConfig.properties"/>

JdbcConfig.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
username=root
password=root

typeAliases标签 和 package标签

SqlMapConfig.xml

使用typeAliases配置别名,只能配置entity包中的别名

<typeAliases>
    <!--<typeAlias type="com.hzj.entity.User" alias="user"/>-->
    <package name="com.hzj.entity"/>
</typeAliases>

typeAlias用于配置别名

type属性指的是全限定类名

alias属性指定的是别名,当指定了别名,就不再区分大小写

package用于指定要配置别名的包 当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写

<mappers>
    <!--<mapper resource="com/hzj/dao/IUserDao.xml"/>-->
    <!--用于指定dao接口所在的包,当指定了之后就不需要在写resource或者class了-->
    <package name="com.hzj.dao"/>
</mappers>

连接池

mybatis连接池提供了3种方式的配置

​ 配置文件的位置

  • 主配置文件SqlMapConfig.xml中的 dataSource 标签,type 属性表示采用何种链接方式。
    • 取值:
      • POOLED 采用传统的javax.sql.dataSource规范中的连接池。mybatis中有针对规范的实现。
      • UNPOOLED 采用传统的获取链接的方式,虽然也实现了javax.sql.dataSource接口,但是并没有使用池的思想。
      • JNDI 采用服务器提供的JNDI技术实现,来获取dataSource对象。不同的服务器所能拿到的dataSource是不一样的。注意:如果不是web或者maven的war工程,是不能使用的。使用的Tomcat服务器,采用的就是dbcp连接池。

事务控制

事务是恢复和并发控制的基本单位。

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性

  • 原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
  • 一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
  • 隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

解决办法:四种隔离级别

mybatis中通过SqlSession对象的 commit() 方法和 rollback() 方法实现事务的提交和回滚。

基于xml配置的动态sql语句

if标签

<!--根据条件查询-->
<select id="findByCondition" resultType="user" parameterType="user">
    select * from user where 1 = 1
    <if test="username != null and username != ''">
        and username = #{username}
    </if>
    <if test="sex != null and sex !=''">
        and sex = #{sex}
    </if>
</select>

where标签

不用写where 1 = 1

<select id="findByCondition" resultType="user" parameterType="user">
    select * from user
    <where>
        <if test="username != null and username != ''">
            and username = #{username}
        </if>
        <if test="sex != null and sex !=''">
            and sex = #{sex}
        </if>
    </where>
</select>

foreach标签

collection 代表要遍历的集合元素

item 代表遍历集合的每个元素,生成的变量名

separator代表分隔符

<!--根据QueryVo中的id集合实现查询用户列表-->
<select id="findInIds" resultType="user" parameterType="QueryVo">
    select * from user
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="and id in(" close=")" item="id" separator=",">
                #{id}<!--和item一样-->
            </foreach>
        </if>
    </where>
</select>

sql标签

抽取重复的sql语句

<sql id="defaultUser">
    select * from user 
</sql>

在别的地方引用

<!--根据id查询用户-->
<select id="findById" parameterType="int" resultType="user">
    <include refid="defaultUser"/> where id = #{id};
</select>

多表操作(待填坑)

一对多

从表实体应该包含一个主表实体的对象引用

一对多关系映射:主表实体应该包含从表的集合引用

多对多

没看懂……以后填坑

延迟加载

延迟加载:在真正使用数据的时候才发起查询,不用的时候不查询。按需查询(懒加载)

立即加载:不管用不用,只要一调用方法,马上发起查询

在对应的四种表关系中:

  • 一对多、多对多:通常采用延迟加载。
  • 多对一、一对一:通常情况下,采用立即加载。

在主配置文件 SqlMapConfig.xml中开启mybatis支持延迟加载

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

多对一的延迟加载

  • 查询账户对应的用户
<!--定义封装Account和User的ResultMap-->
<resultMap id="accountUserMap" type="account">
    <id property="id" column="id"/>
    <result property="uid" column="uid"/>
    <result property="money" column="money"/>
    <!--一对一的关系映射-->
    <association property="user" column="uid" javaType="user" select="com.hzj.dao.IUserDao.findById"/>
</resultMap>

<!--查询所有-->
<select id="findAll" resultMap="accountUserMap">
    select *
    from account;
</select>

select:对应的查找一个的方法 (好像是这样。不确定)

mybatis中的缓存

一级缓存

  • mybatis中一级缓存指的是SqlSession对象的缓存。
  • 执行查询结果后,查询的结果会存入到SqlSession提供的一片区域中。
  • 该区域的结构是一个Map。当再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话那来直接用
  • 当SqlSession对象消失时,mybatis的一级缓存也就消失了。

代码实现

@Test
public void testFirstLevelCache() {
    User u1 = dao.findById(41);
    System.out.println(u1.hashCode());
    User u2 = dao.findById(41);
    System.out.println(u2.hashCode());
    System.out.println(u1 == u2);
}

​ 输出结果

com.hzj.entity.User@2e6a8155
com.hzj.entity.User@2e6a8155
true

​ 关闭sqlSession清空缓存

@Test
public void testFirstLevelCache() {
    User u1 = dao.findById(41);
    System.out.println(u1.hashCode());

    session.close();
    session = factory.openSession();
    dao = session.getMapper(IUserDao.class);

    User u2 = dao.findById(41);
    System.out.println(u2.hashCode());
    System.out.println(u1 == u2);
}

​ 输出结果

com.hzj.entity.User@2e6a8155
中间再次执行了一次sql语句
com.hzj.entity.User@52719fb6
false

session.clearCache();//此方法也可以清空缓存

触发清空一级缓存的情况

当调用SqlSession的 修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

二级缓存

  • mybatis中二级缓存指的是SqlSessionFactory对象的缓存。由同一个SqlSessionFactory创建的SqlSession共享其缓存。

  • 二级缓存的使用步骤

    1. 让mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    <settings>
         <setting name="cacheEnabled" value="true"/>
      </settings>
    
    1. 让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
    <!--开启user支持二级缓存-->
      <cache/>
    
    1. 让当前的操作支持二级缓存(select标签中配置)

      <select>标签上添加 `useCache="true"` 属性
      
  • 注意: 在mybatis中使用二级缓存就必须要把实体类序列化 implements Serializable

代码实现

public void testSecondLevelCache() {
    SqlSession session1 = factory.openSession();
    IUserDao dao1 = session1.getMapper(IUserDao.class);
    User u1 = dao1.findById(41);
    System.out.println(u1);
    session1.close();//一级缓存消失

    SqlSession session2 = factory.openSession();
    IUserDao dao2 = session2.getMapper(IUserDao.class);
    User u2 = dao2.findById(41);
    System.out.println(u2);
    session2.close();//一级缓存消失

    System.out.println(u1 == u2);
}

输出结果

com.hzj.entity.User@60099951
com.hzj.entity.User@f78a47e
false

  • 二级缓存中存放的是数据,而不是对象。虽然没有发起查询,但不是同一个对象,所以输出false了。

注解开发

单表增删改查

IUserDao.java

public interface IUserDao {

    @Select("select * from user")
    List<User> findAll();

    @Insert("insert into user (username, sex) values (#{username}, #{sex})")
    void save(User user);

    @Update("update user set username = #{username}, sex = #{sex} where id = #{id} ")
    void update(User user);

    @Delete("delete from user where id = #{id}")
    void delete(int id);

    @Select("select * from user where id = #{id}")
    User findById(int id);

    @Select("select * from user where username like #{username}")
    //@Select("select * from user where username like '%${value}%'")
    List<User> findByName(String username);

    @Select("select count(*) from user")
    int findTotal();
}

如果实体类和数据库列名不对应

​ 在方法上添加一下注解

@Results(id = "userMap", value = {
        @Result(id = true, property = "uid",column = "id"),
        @Result(property = "userName",column = "username"),
        @Result(property = "userSex",column = "sex"),
})

​ 然后在其他方法上就可以用 @ResultMap 注解 指定id的属性

多表查询的操作

缓存的配置

在IUserDao.java上添加 @CacheNamespace(blocking = true) 注解,即可开启二级缓存

posted @ 2021-03-30 20:42  夕子学姐  阅读(94)  评论(0)    收藏  举报