案例源码和sql文件

链接:https://pan.baidu.com/s/1JMiiW0sOFfzUXuNBgR-b4Q
提取码:lpqb
复制这段内容后打开百度网盘手机App,操作更方便哦
mybatis-init文件中是MyBatis环境搭建和入门案例、CRUD操作章节的源码
mybatis-dynamicSql文件中是动态SQL章节的源码
mybatis-noe2many文件中是MyBatis中的多表查询一对一和一对多章节的源码

MyBatis环境搭建和入门案例

使用XML的方式

导入jar包坐标:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
</dependency>

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"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="13456"/>
            </dataSource>
        </environment>
    </environments>

    <!--指定映射配置文件的位置-->
    <mappers>
        <mapper resource="cn/example/dao/IUserDao.xml"></mapper>
    </mappers>
</configuration>

User :

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    //getter和setter……
}

IUserDao:

public interface IUserDao {
    List<User> findAll();
}

IUserDao.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="cn.example.dao.IUserDao">
    <select id="findAll" resultType="cn.example.domain.User">
        select * from user
    </select>
</mapper>

测试:

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

使用注解的方式

使用注解的方式只需做如下两点修改
IUserDao :

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

sqlMapConfig.xml配置文件:
只需将resource属性改为class属性指定dao接口所在包位置

<mappers>
    <mapper class="cn.example.dao.IUserDao"></mapper>
</mappers>

CRUD操作

基本的CRUD操作

IUserDao 接口:

public interface IUserDao {
    //查询所有用户
    List<User> findAll();

    //保存用户
    void saveUser(User user);

    //更新
    void updateUser(User user);

    //根据id删除
    void deleteUser(Integer id);

    //根据id查找
    User findOneByid(Integer id);

    //根据姓名模糊查询
    List<User> findByName(String name);

    //查询总记录数
    Integer findTotal();
}

IUserDao.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="cn.example.dao.IUserDao">
    <select id="findAll" resultType="cn.example.domain.User">
        select * from user
    </select>

    <insert id="saveUser" parameterType="cn.example.domain.User">
        <!-- 配置插入操作后,获取插入数据的自增id -->
        <selectKey keyProperty="id" keyColumn="id" resultType="Integer" order="AFTER">
            select last_insert_id();
        </selectKey>
        insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})
    </insert>

    <update id="updateUser" parameterType="cn.example.domain.User">
        update user set username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} where id = #{id}
    </update>

    <!--当只有一个基本类型的参数时,#{}中的值可以任意取名-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id = #{id}
    </delete>

    <select id="findOneByid" parameterType="java.lang.Integer" resultType="cn.example.domain.User">
        select * from user where id = #{id}
    </select>

    <select id="findByName" parameterType="String" resultType="cn.example.domain.User">
        <!--  select * from user where username like #{name}
            推荐使用上面这种写法,因为${}采用的是字符串替换,#{}采用的是占位符?
        -->
        <!--  value是固定的,不能换成其他名称  -->
        select * from user where username like '%${value}%'
    </select>

    <select id="findTotal" resultType="Integer">
        select count(*) from user
    </select>
</mapper>

传递pojo包装对象作为查询条件

MyBatis使用OGNL表达式解析对象字段的值,#{}或者${}括号中的值为pojo属性名称

OGNL表达式全称Object Graphic Navigation Language,对象图导航语言,它是通过对象的取值方法来获取数据,在写法上省略了getXXX()。比如User中提供了getUsername()方法获取username属性值,使用OGNL表达式写法就是user.username获取属性值。
而在MyBatis中,直接写username,而不是user.username,因为在parameterType属性中已经提供了属性所属的类,所以此时不需要写对象名称,如<insert id="saveUser" parameterType="cn.example.domain.User"> insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address}) </insert>

IUserDao接口中增加方法:

//根据QueryVo查询用户
List<User> findUserByVo(QueryVo queryVo);

QueryVo:

public class QueryVo {
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

IUserDao.xml中增加mapper配置:

<select id="findUserByVo" parameterType="cn.example.domain.QueryVo" resultType="cn.example.domain.User">
    select * from user where username like #{user.username}
</select>

结果类型的封装

OGNL表达式能够根据传递过来的User对象,将user.username替换到#{username}上的前提是User对象的username属性和#{username}中的名称相同,如果将User对象中的username属性改为name,那么就需要在sql语句中写成#{name},但是对于返回值类型,因为数据库中的字段还是username,这还能将name值封装到User对象的username属性中吗?答案是不行。

在Windows系统上,MySQL数据库不区分大小写,即username和userName是同一个字段,因此User对象中的属性是userName,数据库中的字段可以是username,当从数据库查询到结果的时候,username字段和userName属性能够匹配上从而封装到User对象中。

解决数据库字段和对于的实体属性名称不同的办法有两种:

  1. 写sql的时候给结果字段起别名,即select user as username ...
    执行效率高
  2. MyBatis提供的配置项resultMap
    开发效率高

使用resultMap

修改User类:

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    //getter和setter方法省略...    
}

IUserDao.xml:

<!--配置结果集列名和实体类的属性对应关系-->
<resultMap id="userMap" type="cn.example.domain.User">
    <!--主键字段的对应-->
    <id property="userId" column="id"></id>
    <!--非主键字段的对应-->
    <result property="userName" column="username"></result>
    <result property="userBirthday" column="birthday"></result>
    <result property="userSex" column="sex"></result>
    <result property="userAddress" column="address"></result>
</resultMap>

<select id="findAll" resultMap="userMap">
    select * from user
</select>

<insert id="saveUser" parameterType="cn.example.domain.User">
    <!-- 配置插入操作后,获取插入数据的自增id -->
    <selectKey keyProperty="userId" keyColumn="id" resultType="Integer" order="AFTER">
        select last_insert_id();
    </selectKey>
    insert into user(username, birthday, sex, address) values(#{userName}, #{userBirthday}, #{userSex}, #{userAddress})
</insert>

sqlMapConfig.xml相关配置

properties标签的resource、url属性用法

使用properties标签的resource属性和url属性可以指定数据库的配置文件
resources路径下 jdbcConfig.properties:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy_mybatis
jdbc.username=root
jdbc.password=123456

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
        可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息
        resource属性:
            用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
        url属性:
            是要求按照Url的写法来写地址
    -->
    <!--<properties url="jdbcConfig.properties在磁盘上的路径"></properties>-->
    <properties resource="jdbcConfig.properties"></properties>
    <!--配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>
    
	<!--指定映射配置文件的位置-->
    <mappers>
        <mapper resource="cn/example/dao/IUserDao.xml"></mapper>
    </mappers>
</configuration>

typeAliases标签

使用typeAliases可以给实体类起别名

<typeAliases>
    <!--typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就会区分大小写 
    <typeAlias type="cn.example.domain.User" alias="user"></typeAlias>-->

    <!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
    <package name="cn.example.domain"></package>
</typeAliases>

在IUserDao.xml中指定parameterType的时候使用user即可,无需指定全限定类名

<update id="updateUser" parameterType="user">
    update user set username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} where id = #{id}
</update>

mappers标签

<!--指定映射配置文件IUserDao.xml的位置-->
<mappers>
    <mapper resource="cn/example/dao/IUserDao.xml"></mapper>
</mappers>
<!--指定dao接口的位置-->
<mappers>
    <mapper class="cn.example.dao.IUserDao"></mapper>
</mappers>
<!--无论是xml的配置还是注解的配置,都可以使用package标签-->
<mappers>
    <package name="cn.example.dao"></package >
</mappers>

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 resource="jdbcConfig.properties"></properties>
    <typeAliases>
        <package name="cn.example.domain"></package>
    </typeAliases>
    <environments default="mysql">
        <!--mysql的配置环境-->
        <environment id="mysql">
            <!--配置事务类型-->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>

    <!--指定映射配置文件的位置-->
    <mappers>
        <package name="cn.example.dao"></package >
    </mappers>
</configuration>

MyBatis中的连接池

<dataSource type="POOLED">
...
</dataSource>

dataSource的type属性指定了连接池类型,可以取值:

  • POOLED
    采用传统的javax.sql.DataSource规范中的连接池,mybatis中针对规范的实现实现了一套自己的连接池技术
  • UNPOOLED
    采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是并没有采用池的概念,即每次需要连接的时候建立连接,用完释放连接,而不是归还到池中
  • JNDI
    采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到的DataSource不同,tomcat服务器采用的是dbcp连接池。如果不是web或者maven的war工程,不能使用JNDI

MyBatis中的事务控制

openSession()方法中传入参数true,可以设置事务提交方式为手动提交,不传参数则为自动提交

 //1. 读取配置文件
 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
 //2. 创建SqlSessionFactory工厂
 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
 SqlSessionFactory factory = builder.build(inputStream);
 //3. 使用工厂生产SqlSession对象
 SqlSession  sqlSession = factory.openSession(true);
 //4. 使用SqlSession对象创建Dao接口的代理对象...
 //5. 通过代理对象执行方法...
 //6. 释放资源...

动态SQL

User:

public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    //getter和setter、toString方法省略
}

QueryVo :

public class QueryVo {
    private User user;
	//getter和setter方法省略
}

IUserDao:

public interface IUserDao {

}

IUserDao.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="cn.example.dao.IUserDao">
    <!--配置结果集列名和实体类的属性对应关系-->
    <resultMap id="userMap" type="user">
        <!--主键字段的对应-->
        <id property="userId" column="id"></id>
        <!--非主键字段的对应-->
        <result property="userName" column="username"></result>
        <result property="userBirthday" column="birthday"></result>
        <result property="userSex" column="sex"></result>
        <result property="userAddress" column="address"></result>
    </resultMap>
    
</mapper>

if标签

IUserDao接口中增加方法:

//根据传入的条件查询
List<User> findUserByCondition(User user);

IUserDao.xml中增加配置:

<select id="findUserByCondition" parameterType="user" resultMap="userMap">
    select * from user where 1=1
    <if test="userName != null">
        and username = #{userName}
    </if>
	<if test="userSex != null">
		and usersex = #{userSex}
	</if>
</select>

where标签

<select id="findUserByCondition" parameterType="user" resultMap="userMap">
    select * from user
    <where>
        <if test="userName != null">
            and username = #{userName}
        </if>
        <if test="userSex != null">
            and usersex = #{userSex}
        </if>
    </where>
</select>

foreach和sql标签

select * from user where id in(1,2);

QueryVo中增加属性并生成getter和setter方法:

private List<Integer> ids;

IUserDao接口中增加方法:

//根据QueryVO中提供的ids集合查询用户
List<User> findUserInIds(QueryVo queryVo);

IUserDao.xml中增加配置:

<select id="findUserInIds" parameterType="queryVo" resultMap="userMap">
    select * from user
    <where>
        <if test="ids != null and ids.size()>0">
        	<!-- #{uid}中的uid名称和item值一样 -->
            <foreach collection="ids" open="and id in(" close=")" item="uid" separator=",">
                #{uid}
            </foreach>
        </if>
    </where>
</select>

抽取重复的sql语句

<select id="findAll" resultMap="userMap">
    select * from user
</select>

可以改写为如下,以后用到select * from user的地方都可以用<include refid="defaultUser"></include>代替:

<sql id="defaultUser">
   select * from user
</sql>
<select id="findAll" resultMap="userMap">
    <include refid="defaultUser"></include>
</select>

MyBatis中的多表查询

User:

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    //getter和setter、toString方法省略
}

Account :

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    //getter和setter、toString方法省略
}

IUserDao :

public interface IUserDao {
    //查询所有用户以及用户下所有的账户信息(一对多)
    List<User> findAll();
}

IAccountDao:

public interface IAccountDao {
	//查询所有账户以及账户对应的用户(一对一)
    List<Account> findAll();
}

一对一

关键标签:association

Account类中增加属性,并设置getter和setter方法:

private User user;

IAccountDao.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="cn.example.dao.IAccountDao">

    <!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容
            property:属性名称
            column:联合查询时,account表连接字段名称
            javaType:属性的类型
        -->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="accountUserMap">
        select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
    </select>
</mapper>

一对多

关键标签:collection

User类添加属性:

private List<Account> accounts;
<?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.example.dao.IUserDao">

    <!--一对多映射:定义user的resultMap-->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <result column="sex" property="sex"></result>
        <result column="birthday" property="birthday"></result>
        <!--配置user对象中accounts集合的映射
            property:属性名称
            ofType:集合中元素的类型
        -->
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="userAccountMap">
        select * from user u left join account a on u.id=a.uid
    </select>
</mapper>

多对多

多对多可以看成是一对多,在表结构上是多对多的关系,比如用户和角色,一个用户可以有多个角色,一个角色可以有多个用户,但是在sql层面,它们就是一对多的关系,一个用户可以有多个角色。因此写法和一对多的类似,只是slq语句上有变化。

MyBatis立即加载和延迟加载

《MyBatis立即加载和延迟加载》

MyBatis缓存

MyBatis使用数据缓存减少和数据库的交互次数,从而提高程序执行效率

适用于缓存的情况:

  • 经常查询并且不经常改变的数据
  • 能容忍缓存和数据库数据的不一致

一级缓存

一级缓存指的是MyBatis中SqlSession对象的缓存,当我们执行查询之后,查询的结果会同时存入到SqlSession提供的一块区域中,该区域的结构是一个Map。当我们再次查询同样的数据时,MyBatis会先去SqlSession查询是否有该数据,如果有则直接拿来用。当SqlSession对象消失时,MyBatis的一级缓存消失。

public void testFindOneByid() {
    User user1 = userDao.findOneByid(41);
    User user2 = userDao.findOneByid(41);
    System.out.println(user1 == user2); //true
}

执行了一次sql:
在这里插入图片描述

public void testFindOneByid() {
    User user1 = userDao.findOneByid(41);
    //关闭SqlSession,缓存清空
    sqlSession.close();
    sqlSession = factory.openSession();
    userDao = sqlSession.getMapper(IUserDao.class);
    User user2 = userDao.findOneByid(41);
    System.out.println(user1 == user2); //false
}

执行了两次sql:
在这里插入图片描述
一级缓存清空时机:
一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。因此查询-修改-查询,第二次查询走的是查数据库而不是查缓存。

二级缓存

二级缓存指的是MyBatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享同一个缓存。
使用二级缓存:
sqlMapConfig.xml中添加配置:

<settings>
   <setting name="cacheEnabled" value="true"/>
</settings>

IUserDao.xml中开启支持二级缓存:

<mapper namespace="cn.example.dao.IUserDao">
    <!--开启支持二级缓存-->
    <cache/>
    <select id="findOneByid" parameterType="java.lang.Integer"  resultType="user" useCache="true">
        select * from user where id = #{id}
    </select>
</mapper>

使用:

public class SecondLevelCacheTest {

    private InputStream in;
    private  SqlSessionFactory factory;

    @Before//用于在测试方法执行之前执行
    public void init()throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);

    }

    @After//用于在测试方法执行之后执行
    public void destroy()throws Exception{
        in.close();
    }

    @Test
    public void testFirstLevelCache(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findOneByid(41);
        System.out.println(user1);
        sqlSession1.close();

        SqlSession sqlSession2 = factory.openSession();
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findOneByid(41);
        System.out.println(user2);
        sqlSession2.close();

        System.out.println(user1 == user2); //false
    }
}

在这里插入图片描述
user1和user2值不相等,但是第二次查询的确查的是缓存,这是为什么?因为MyBatis中的二级缓存保存的是数据而不是地址,当程序缓存中有的内存时,将该内容复制一份给新的变量,因此user1和user2的地址值不同。


本文转载:CSDN博客