Mybatis

MyBatis

iBatis -> apache MyBatis
使用步骤:

  1. 添加依赖
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
  1. 添加配置文件 mybatis-config.xml
    用这个配置文件来告诉 mybatis 如何连接数据库
    告诉 mybatis 到哪里去找映射关系
.....

<mappers>
        <!-- 指定映射文件的位置 -->
        <mapper resource="mapper/StudentMapper.xml"/>
</mappers>
  1. 提供一个 映射文件
    用来管理 sql 语句,描述 sql 语句与数据库表之间的映射关系
<?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="命名空间">
    <insert id="sql的名字" parameterType="参数类型" useGeneratedKeys="true" keyProperty="主键对应的属性">
    <update id="sql的名字" parameterType="参数类型">
    <delete id="sql的名字" parameterType="参数类型">
    <select id="sql的名字" parameterType="参数类型" resultType="结果类型,如果是集合是集合中元素的类型">

    <if test="条件"> sql 片段 </if>
    <set></set>
</mapper>

参数类型:
int, string, #{名字任意}
map #{key}
自定义javabean #{属性名}

  1. 调用 mybatis api 使用映射文件真正执行增删改查
InputStream in = Resources.getResourceAsStream("配置文件的路径");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = factory.openSession();
sqlSession
    .insert("命名空间+sql的名字", sql需要的参数对象)
    .update("命名空间+sql的名字", sql需要的参数对象)
    .delete("命名空间+sql的名字", sql需要的参数对象)
    .selectOne("命名空间+sql的名字", sql需要的参数对象)  结果只有一个时 
    .selectList("命名空间+sql的名字", sql需要的参数对象) 结果是list集合

对增删改
sqlSession.commit();

关闭
sqlSession.close();

3. Mybatis 映射文件(重点)

新增

<!-- #{sname} 用来获取 Student 参数对象中的 sname属性-->
<!-- useGeneratedKeys="true" 是告诉 mybatis 要使用由数据库产生的主键值 -->
<!-- keyProperty="主键对应的属性名" -->
<insert id="abc" parameterType="domain.Student"
        useGeneratedKeys="true" keyProperty="sid">
    insert into student(sid, sname, birthday, sex)
        values ( null, #{name}, #{birthday}, #{sex})
</insert>

删除

<delete id="delete" parameterType="int">
    delete from student where sid = #{sid}
</delete>

查询所有

<select id="findAll" resultType="domain.Student">
    select sid,sname name,birthday,sex from student
</select>

根据id查询

<select id="findById" resultType="domain.Student" parameterType="int">
    select sid,sname name,birthday,sex from student where sid = #{sid}
</select>

更新

<update id="update" parameterType="domain.Student">
    update student set sname=#{name}, birthday=#{birthday}, sex=#{sex} where sid=#{sid}
</update>
<!-- 动态更新列
Student stu = new Student();
stu.setSid(1001);
stu.setSex("男");

update student set sex=#{sex} where sid=#{sid}
用  set 标签可以去除多余的逗号
-->
<update id="update" parameterType="domain.Student">
    update student
    <set>
        <if test="name != null">
            sname=#{name},
        </if>
        <if test="birthday != null">
            birthday=#{birthday},
        </if>
        <if test="sex != null">
            sex=#{sex},
        </if>
    </set>
    where sid=#{sid}
</update>

分页查询

<!-- m , n
java.util.Map -> map
java.util.List -> list
java.lang.String -> string
map.put("m", 0);
map.put("n", 5);
-->
<select id="findByPage" parameterType="map" resultType="domain.Student">
    select sid,sname name,birthday,sex from student limit #{m}, #{n}
</select>

动态 sql 生成

  1. foreach 循环

delete from 表 where id in(1008,1011,1012);

<!-- list (1008,1011,1012, 1013) -->
<delete id="" parameterType="list">
delete  from 表 where id in  
	<foreach collection="list" item="x" open="(" close=")" separator=",">#{x}</foreach>
</delete>
  1. if where 生成动态sql
 <select id="findByCondition" parameterType="map" resultType="domain.Student">
        select sid,sname name,birthday,sex from student
        <where> <!-- 可以用 where 去除多余的 and -->
            <if test="sname!=null">
                and sname = #{sname}
            </if>
            <if test="birthday!=null">
                and birthday = #{birthday}
            </if>
            <if test="sex != null">
                and sex = #{sex}
            </if>
        </where>
    </select>
  1. 对于大于小于条件的处理
    方法1: 对每个特殊字符转转义,例如 < – &lt;
    方法2: <![CDATA[ 内容 ]]>

  2. #{ } 与 ${ } 的区别

  • #{ } 底层是替换为 ?, 只能占位值
  • ${ } 底层是直接拼接字符串, 有注入攻击问题
  • 尽量使用 #{ } , 只有在某些功能不能用 #{ } 实现时(例如排序)采用 ${ }

4. 通过日志工具监控mybatis生成的sql语句

logback

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

添加一个 logback.xml 到 resources文件夹

<!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) -->
<logger name="mapper.StudentMapper" level="DEBUG" additivity="false">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="FILE"/>
</logger>

5. Mapper 接口

在mybatis-config.xml中添加映射关系

<mappers>
<!-- 指定映射接口的 包名.类名 -->
<mapper class="mapper.StudentMapper"/>
</mappers>
@Insert @Option
@Update
@Delete
@Select

src/main/java目录下新建mapper目录将所有的mapper放在该目录下。
所有的mapper都为接口类。

public interface StudentMapper {

    @Insert("insert into student(sid,sname,birthday,sex) values(null,#{sname}, #{birthday}, #{sex})")
    @Options(useGeneratedKeys = true, keyProperty = "sid")
    void insert(Student student);

    @Update("update student set sname=#{sname}, birthday=#{birthday}, sex=#{sex} where sid=#{sid}")
    void update(Student student);

    @Delete("delete from student where sid=#{sid}")
    void delete(int sid);

    @Select("select * from student")
    List<Student> findAll();

    @Select("select * from student where sid = #{sid}")
    Student findById(int sid);

    @Select("select * from student limit #{m}, #{n}")
    List<Student> findByPage(Map map);

    // 同时根据姓名和性别查询
    @Select("select * from student where sname=#{a} and sex=#{b}")
    List<Student> find1(@Param("a") String sname, @Param("b") String sex);
    @Select("select * from student where sname=#{a} and sex=#{b}")
    List<Student> find2(Map<String, Object> map);

    void deleteByIds(List<Integer> ids);

}

原理

// 这个类是使用了 jdk的动态代理技术 在代码运行期间生成的类 
public class $Proxy10 implements StudentMapper{
    private SqlSession sqlSession;
    public $Proxy10(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
    // 其中 sql语句从@Insert注解获得, 参数对象就是student
    public void insert(Student student) {
        sqlSession.insert(sql, 参数对象)
    }

    // 其中 sql语句从@Select注解获得
    public List<Student> findAll() {
        return sqlSession.selectList(sql);
    }
}

3. Mapper接口不足

1. 接口方法不能直接应用多个方法参数

解决方法:

  1. 用map传递多个参数, 每个参数对应map中的一个键值对
  2. 用@Param注解(上面例子中姓名与性别同时查询)

2. Mapper 接口中不能有方法的重载

定义方法是不要方法名冲突
异常信息:

### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for 接口名.方法名(重复的)

3. 使用Mapper接口方式实现动态sql比较复杂

方法1: 结合Mapper接口和xml文件

  1. 首先在接口类中定义方法,具体方法在xml文件中实现。
  2. mapper的xml中namespace必须与接口类路径名保持一致。
  3. 不用再mybatis-config.xml中添加xml的映射关系。xml使用的是接口类的映射。
    方法2:
    复杂sql映射
    除了使用 xml mapper 以外,还可以利用 @DeleteProvider @InsertProvider @SelectProvider @UpdateProvider 生成复杂sql。
public interface StudentMapper {
    // 由 @DeleteProvider 注解找到一条sql语句,供 deleteByIds 方法使用
    @DeleteProvider(
            type = StudentSqlProvider.class,
            method = "getDeleteByIdsSql"
    )
    void deleteByIds(List<Integer> ids);
}
public class StudentSqlProvider {
    public static String getDeleteByIdsSql(@Param("list") List<Integer> aaa) {
        StringBuilder sb = new StringBuilder(128);
        sb.append("delete from student where sid in");
        sb.append("(");
        for (int i = 0; i < aaa.size(); i++) {
            sb.append(aaa.get(i));
            if(i < aaa.size()-1) {
                sb.append(",");
            }
        }
        sb.append(")");
        return sb.toString();
    }
}

4. 高级映射

表列 与 属性名 不一致 ==> 给列起别名

public interface ModuleMapper {
    // 查询所有一级模块,并包含每个二级模块
    public List<Module> findAll();
}
<!-- resultMap 指定返回结果中的对应关系 -->
<select id="findAll" resultMap="b">
        select a.*, b.id bid, b.name bname, b.pid bpid, b.code bcode
          from rbac.rbac_module a inner join rbac.rbac_module b on a.id = b.pid
    </select>
    <!--  id  主键映射
    	result 非主键映射(若列名与属性名相同 用autoMapping="true" 可不用写)
    	collection 为集合映射
	-->
    <resultMap id="b" type="domain.Module" autoMapping="true">
        <!-- 映射一级模块的属性 -->
        <id column="id" property="id"/>
        <!-- ofType="集合中的元素类型" -->
        <collection property="children" ofType="domain.Module">
            <!--映射 二级模块的属性 -->
            <id column="bid" property="id"/>
            <result column="bname" property="name"/>
            <result column="bpid" property="pid"/>
            <result column="bcode" property="code"/>
        </collection>
    </resultMap>

上边sql语句查出的表如图:
在这里插入图片描述
也可以使用多个查询语句

<select id="findAll" resultMap="b">
        select id,name,pid,code from rbac.rbac_module where pid=0
    </select>
    
    <resultMap id="b" type="domain.Module">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pid" property="pid"/>
        <result column="code" property="code"/>
       <!-- 对 children 属性做映射
                集合属性使用 collection标签映射
                property="属性名字"
                select="要执行的sql语句名"
                column="作为条件的列"
       -->
        <collection property="children" select="findModule2" column="id"/>
    </resultMap>

    <select id="findModule2" parameterType="int" resultType="domain.Module">
        select id,name,pid,code from rbac.rbac_module where pid = #{pid}
    </select>

5缓存

select * from student where id = 1001 student

select * from student where id = 1001

  • sqlsession 自带缓存功能,缓存没有才查库,缓存中有,直接返回缓存的结果,称为一级缓存
  • 在sqlsession创建时建立,sqlsession.close 时清空
  • 每个sqlsession有自己独立的缓存

二级缓存
需要额外设置,但可以允许多个 sqlsession 共享缓存数据

好处: 可以较大提升查询效率, 但是增删改频繁的情况下,不适合开启二级缓存

xml mapper 二级缓存的配置: 在 xml 映射文件中添加<cache/>标签即可
接口 mapper 二级缓存的配置

6. 学习资料

  • http://www.mybatis.org/mybatis-3/zh/index.html 官方文档,偏向基础
  • 阅读源码
  • 在应用上更近一步 mybatis-plus 是mybatis的扩展
    https://mp.baomidou.com/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值