SSM框架之Mybatis

一、Mybatis概述

1.1 什么是Mybatis?

  • mybatis框架内部封装了jdbc,开发者只需要关注sql语句本身(更加关注业务需求而不是去关注其他的建立连接、释放资源等繁琐操作)mybatis通过xml或注解的方式将需要执行的各种statement配置起来,通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果集映射为java对象返回
  • Mybatis中sql与Java编码分离,sql以配置文件的形式编写,由开发人员控制。其它预编译、设置参数、执行sql、封装结果、释放资源等由框架执行,属于一个轻量级的半自动持久层框架

1.2 Mybatis的依赖

  • Maven导入坐标
<dependencies>
    <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>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
    </dependency>
</dependencies>

二、基于XML配置Mybatis

2.1 基本步骤

  1. 创建maven工程并导入坐标
  2. 创建实体类(用来映射数据库中的对象)和dao(数据访问层)的接口
  3. 创建mybatis的主配置文件:SqlMapConfig.xml
  4. 创建映射配置文件:UserDao.xml

2.2 环境搭建注意事项

  1. mybatis的映射配置文件位置必须和dao接口的包结构相同(相同目录结构)

    ​ 因为反射在读取配置文件时,对于maven工程,反射的路径是target\classes...,包结构相同,才能读取到xml文件

  2. 映射配置文件的Mapper标签namespace属性的取值必须是dao接口的全限定类名,以名称空间区分同名的类

  3. 映射配置文件的操作配置(select\update等标签),id属性的取值必须是dao接口的方法名,当遵从了以上配置后,在开发中就无须再写dao的实现类,由mybatis通过反射创建代理类。

  4. 参考:Mybatis映射配置文件和接口名称一致性的问题


2.3 配置示例

1.主配置文件

  • 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">
<-- mybatis的主配置文件,主要用来和数据库建立连接,另外还要建立和sql映射文件的连接 -->
<configuration>
    <-- 配置默认环境 -->
    <environments default="mysql">
    
        <-- 配置mysql的环境 -->
        <environment id="mysql">
    
            <-- 配置事务的类型 -- >
            <transactionManager type="JDBC"></transactionManager>
    
                <-- 配置数据源(连接池)-->
                <dataSource type="POOLED">
                <-- 配置连接数据库的4个基本信息-->
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql:///mybatis"/>
                    <property name="username" value="root"/>
                    <property name="password" value="1234"/>
                </dataSource>
        </environment>
    </environments>
    <mappers>
    < -- 指定映射配置文件,映射配置文件指的是每个dao独立的配置文件,可以用packge直接指定某个包-->       
    <mapper resource="com/itheima/dao/UserDao.xml"/>  //在main方法中只读取了主配置文件,因此需要把映射配置文件也放进来,映射配置文件的url,写路径的时候
    </mappers>                                                                                                                      
</configuration>
  • 数据库连接可用propeties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///mybatis
username=root
password=1234
<properties resource="MapConfig.properties"/>
<dataSource type="POOLED">
    <!--配置连接数据库的4个基本信息-->
    <property name="driver" value="${driver}"/>   //固定写法,${properties文件中的键名}
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

2.Dao接口配置文件

  • Sql映射文件UserDao配置
<?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.itheima.dao.UserDao">  //全限定类名
    <-- 配置查询所有-->
    <select id="findAll" resultType="com.itheima.domain.User"> //映射到实体类
    select * from employee
    </select>
</mapper>

3.Dao接口配置

  • 配置Dao接口
public interface UserDao {
    /*
    * 查询所有操作
    * */
    List<User> findAll();
}

4.测试类

  • 测试
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对象,true开启自动提交事务的功能
        SqlSession session = factory.openSession(true);
        // 4、使用SqlSession创建Dao接口的动态代理对象
        UserDao dao = session.getMapper(UserDao.class);
        // 5、使用代理对象执行方法
        List<User> users = dao.findAll();
        // 输出对象
        for (User user : users)
            System.out.println(user);
        // 6、释放资源
        session.close();
        inputStream.close();
    }
}

三、基于注解配置Mybatis

  • Mybatis的接口配置只能选择注解或者xml之一,不能同时存在。

1.配置全局配置文件

​ 修改映射路径为class。也可以使用过package属性。

<mappers>
<!--指定映射配置文件,映射配置文件指的是每个dao独立的配置文件-->
<!--如果是用注解来配置的话,此处应该左边使用class属性,右边指定被注解的dao全限定名-->
<mapper class="com.itheima.dao.UserDao"/> //因为不是用xml配置,dao接口已经被初始完毕,所以这里需要的是全限定名而不是路径
</mappers>

2.注解配置Dao接口

public interface UserDao {
    /*
    * 查询所有操作
    * */
    @Select("select * from employee") //基于方法的注解,为findAll方法传入了一个属性"select * from employee"
    List<User> findAll();
}

四、综合案例

4.1 配置Dao接口

1.xml

<mapper namespace="com.itheima.mybatis.dao.UserDao">
    <-- sql映射配置文件-->
    < -- 配置查询所有-->
    <select id="findAll" resultType="com.itheima.mybatis.domain.User">
        select * from employee
    </select>

    <update id="update" parameterType="com.itheima.mybatis.domain.User">
        update employee set lastname = #{lastname},gender = #{gender} where id = #{id}
    </update>

    <delete id="delete" parameterType="Integer">
        delete from employee where id = #{id}
    </delete>

    <insert id="insert" parameterType="com.itheima.mybatis.domain.User">
        insert into employee(lastname,gender,salary,age) values(#{lastname},#{gender},#{salary},#{age})
    </insert>
</mapper>

2.注解

public interface UserDao {
    @Select("select * from employee ")
    List<User> findAll();

    //注意注解中的属性一定要和User中的属性名称相同,并且只有当User中的getter&setter方法是自动生成的,才可以映射成功,#{}表示占位符,里面内容要名称一致
    @Update("update employee set lastname = #{lastname},gender = #{gender} where id = #{id}") //#{}大括号里面的内容要和JavaBean属性名一致,因为这里的值是调用      
    public void update(User user);                                                             getter方法获得的

    @Insert("insert into employee(lastname,gender,salary,age) values(#{lastname},#{gender},#{salary},#{age})")
    public void insert(User user);

    @Delete("delete from employee where id = #{id}") //当只有单个占位符时,里面的名称可以随意
    public void delete(Integer id);

    @Select("select * from employee where lastname like #{name}") //模糊查询
    public List<User> moHuFind(String s);
}

4.2 测试

public class MybatisTest implements Serializable {
    InputStream inputStream;
    SqlSession sqlSession;
    UserDao userDao;

    @Before  //Before注解的作用是说明在@Test之前执行
    public void init() throws IOException {
        inputStream = Resources.getResourceAsStream(null, "sqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        sqlSession = factory.openSession(true);
        userDao = sqlSession.getMapper(UserDao.class);
    }
    @After  //After注解的作用是说明在@Test之后执行
    public void close() throws IOException {
        sqlSession.close();
        inputStream.close();
    }

    @Test
    public void insert() {
        User user = new User();
        user.setAge(20);
        user.setGender("女");
        user.setLastname("Nana");
        user.setSalary(28300.0);
        userDao.insert(user);
    }

    @Test
    public void update() {
        User user = new User();
        user.setId(8);
        user.setAge(22);
        user.setGender("女");
        user.setLastname("NaNaNa");
        user.setSalary(28400.0);
        userDao.updata(user);
    }

    @Test
    public void delete() {
        userDao.delete(5);
    }

    @Test
    public void query() {
        List<User> list = userDao.findAll();
        System.out.println(list);
    }
    @Test
    public void moHuFind() {
        List<User> list = userDao.moHuFind("%i%");
        System.out.println(list);
    }
}

4.3 数据库和实体类属性名不一致

  • 对于实体类,如果类名称过长,可以在主配置文件中起别名
  • 对于属性名不一致问题,在主配置文件中建立ResultMap进行映射
<typeAliases>
<typeAlias alias="user" type="com.Mybatis.User"/> //alias表示别名,如果不写,默认是对应类的小写名
</typeAliases>
<mapper namespace="com.Mybatis.UserDao">
    
    <resultMap id="users" type="com.Mybatis.User"> //id可以随意取,表示该resultMap的名称
        < -- id表示主键-->
        <id property="userId" column="id"/>  //property:实体类中属性 column:数据库中属性

        <-- result表示非主键-->
        <result property="userName" column="lastname"/>
        <result property="userGender" column="gender"/>
        <result property="userSalary" column="salary"/>
        <result property="userAge" column="age"/>
    </resultMap>
        
    <select id="findAll" resultMap="users"> //这里返回类型要用resultMap而不是resultType
        select * from employee
    </select>

    <select id="findById" parameterType="Integer" resultMap="users">
        select * from employee where id = #{id}
    </select>

    <select id="findByName" parameterType="String" resultMap="users">
        select * from employee where lastname like #{name}
    </select>

    <select id="findByVo" parameterType="com.Mybatis.QueryVo" resultMap="users"> //当为Vo类时,要用Vo类的对象的属性
        select * from employee where lastname = #{user.userName}
    </select>
</mapper>

五、动态SQL语句

  • Mybatis的xml配置提供了一组动态SQL语句,可以提高SQL执行效率

1.if标签

<!--根据条件查询 <if>标签-->
<select id="findUserByCondition" parameterType="user" resultMap="users">
    select * from employee where 1=1
    <if test="userName != null"> //这里要使用实体类中的属性,因为是要判断用户输入的值是否是数据库中的某个值,因此输入的应该是实体类中的属性,所以实际开发中尽量让名称相同
        and lastname = #{userName} //注意要有and连接,整句就相当于select * from employee where lastname = #{userName} (and userName != null)
    </if>
</select>

2. Where标签

<select id="findUserByCondition" parameterType="user" resultMap="users">
    select * from employee
    <where> //where嵌套多个if条件
        <if test="userName != null">
            and lastname = #{userName}
        </if>
        <if test="userId != null">
            and id = #{userId}
        </if>
    </where>
</select>

3. foreach标签

<select id="findUserByList" parameterType="queryvo" resultMap="users">
    select * from employee
    <where>
        <if test="list != null">
            <foreach collection="list" open="and id in(" close=")" item="id" separator=","> //这里的open和close表示开始和结束的符号,sql中语句为 where id in(...)
            #{id}
            </foreach>
        </if>
    </where>
</select>

List<User> findUserByList(QueryVo queryVo); //方法声明,复合查询
public class QueryVo {  //QueryVo中添加list
    User user;
    List<Integer> list;
}

六、Mybatis中的多表查询-xml方式

6.1 一对一

  • 示例中,一个account对应一个user,在account中引用user
<resultMap id="userAccountMap" type="account">
    <!--account中别名-->
    <id property="ID" column="aid"/>  //要注意,主表和从表的id最好起不同名,不然相同的话会出现数据覆盖的BUG,如果同名的话可以在sql语句中用别名区别
    <result property="uid" column="uid"/>
    <result property="money" column="money"/>
    
    <-- 配置一对一关系映射,封装user的内容,association谁,哪个键去关联他,它是什么类型-->
    <association property="user" column="uid" javaType="user">
        <-- 配置封装对象user的resultMap-->
        <id property="id" column="id"/>
        <result property="lastname" column="lastname"/>
        <result property="gender" column="gender"/>
        <result property="salary" column="salary"/>
        <result property="age" column="age"/>
    </association>
</resultMap>

<select id="findAll" resultMap="userAccountMap">
    SELECT * FROM account,employee WHERE account.`uid`=employee.`id`
</select>

6.2 一对多

  • 一个user对应多个account,user has account collection
<!--配置一个用户对应多个账户的一对多连接-->  //Mybatis能够做到对于相同名称的用户,将所属它的Account对象都封装到accounts集合中(通过id和uid的连接关系来判断)
<resultMap id="UserAccountMap" type="user"> 
    <id property="id" column="id"/>
    <result property="lastname" column="lastname"/>
    <result property="gender" column="gender"/>
    <result property="salary" column="salary"/>
    <result property="age" column="age"/>
    <!--配置user对象中账户集合accounts的映射,ofType:集合元素所属类型-->
    <collection property="accounts" ofType="account">
        <id property="ID" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
    </collection>
</resultMap>

<select id="findAll" resultMap="UserAccountMap">
    SELECT * FROM employee LEFT JOIN account ON employee.`id`=account.`uid`  //左外连接,employee做主表
</select>

6.3 多对多

  • 用户和角色:一个用户可以有多重角色(身份),一个角色(身份)可以赋予多个用户,多对多其实就是不同实体类的一对多

    步骤

    1. 建立两张表:用户表和角色表让用户表和角色表具有多对多的关系,需要使用中间表,中间表中包含各自的主键,这些主键在中间表中是外键
    2. 建立两个实体类:用户实体类和角色实体类让用户和角色的实体类能体现出多对多的关系,各自包含对方一个集合引用
    3. 建立两个配置文件:用户和角色的配置文件
    4. 实现配置:当我们查询用户时,可以同时得到用户所包含的角色信息当我们查询角色时,可以同时得到角色所赋予的用户信息

首先创建角色表的一对多

<mapper namespace="com.Mybatis.RoleDao">
    <!--定义Role表中的别名resultMap-->
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="roleID"/>
        <result property="roleName" column="ROLE_NAME"/>
        <result property="roleDesc" column="ROLE_DESC"/>

        <collection property="userList" ofType="user">
            <id property="id" column="id"/>
            <result property="lastname" column="lastname"/>
            <result property="gender" column="gender"/>
            <result property="salary" column="salary"/>
            <result property="age" column="age"/>
        </collection>
    </resultMap>
    <!--查询语句-->
    <select id="findAll" resultMap="roleMap">
        SELECT employee.*,role.ID as roleID,ROLE_NAME,ROLE_DESC FROM
        role LEFT JOIN user_role ON role.`ID`=user_role.`RID`
        LEFT JOIN employee ON employee.`id`=user_role.`UID`
    </select>
</mapper>

然后创建用户表的一对多

<resultMap id="UserRoleMap" type="user">
    <id property="id" column="id"/>
    <result property="lastname" column="lastname"/>
    <result property="gender" column="gender"/>
    <result property="salary" column="salary"/>
    <result property="age" column="age"/>

    <collection property="roleList" ofType="role">
        <id property="roleId" column="roleID"/>
        <result property="roleName" column="ROLE_NAME"/>
        <result property="roleDesc" column="ROLE_DESC"/>
    </collection>
</resultMap>

<select id="findAll" resultMap="UserRoleMap">
    SELECT employee.*,role.`ID` AS roleID,role.`ROLE_DESC`,role.`ROLE_NAME` FROM employee LEFT JOIN user_role ON employee.`id` = user_role.`UID`
    LEFT JOIN role ON role.`ID` = user_role.`RID`
</select>

七、Mybatis中的多表查询-注解方式

1.一对一

  • 一对一配置
  • xml中编写了具体的多表连接语句,而注解中没有编写多表连接语句,因此要手动指定是哪个方法。
@Select("select * from account")        //配置account的映射关系
    @Results(id = "AccountUserMap", value = {
    @Result(id = true,property = "aid",column = "aid"),
    @Result(property = "uid",column = "uid"),
    @Result(property = "money",column = "money"),
    //配置account中一对一的user信息,column是表示关联的字段,通过该字段来获取到对应的user,one表示一对一,里面有个@one注解,包含select参数和fetchType参数
    //select参数是一个sql语句的执行方法的全限定名,因为最终要根据uid查询到对应的user,因此需要的方法是user中的findByID方法,fetchType是加载策略,有LAZY,EAGER,DEFAULT三个值
    @Result(property = "user",column = "uid",one = @One(select = "com.itheima.dao.UserDao.findByID",fetchType = FetchType.EAGER))
})
List<Account> findAll();

2.一对多

  • 一对多配置
  • 同样也要手动指定连接的方法
@Select("select * from employee")
@Results(id = "UserAccountMap", value = {
// 第一行配置主键,id默认为false,配置为true表示该键为主键
    @Result(id = true,property = "id",column = "id"),
    // 配置其他属性对应
    @Result(property = "lastname",column = "lastname"),
    @Result(property = "salary",column = "salary"),
    @Result(property = "gender",column = "gender"),
    @Result(property = "age",column = "age"),
    @Result(property = "accounts",column = "id",
many = @Many(select = "com.itheima.dao.AccountDao.findAccountByID",fetchType = FetchType.EAGER))
})
List<User> findAll();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352