Mybatis整理

一、基础部分

1.目前常用版本为3.4.5

2.主要有如下两个配置文件:mybatis-config.xml文件 xxxMapper.xml文件

2.1 mybatis-config.xml配置:

               1.引入properties文件中配置的参数

                2.设置对应的setting,包括驼峰,自动映射等

        配置示例如下所示:

<!-- aa为从resource下级路径开始指定到xxx.properties  / 分割-->
<properties resource = "aa"></properties>
<settings>
     <!-- 日志输出,使用标准输出(在控制台进行输出)-->
	<setting name = "logImpl" value="STDOUT_LOGGING"></setting>
      <!-- NONE:不自动进行映射 
      PARTIAL:部分映射,不进行嵌套查询的映射,只进行简单查询的映射
      FULL:连着嵌套查询一起映射
      默认是PARTIAL,比较节省性能 -->
    <setting name="autoMappingBehavior" value="PARTIAL"/>
    <!-- 配置驼峰转下划线 数据库中的下划线,转换Java Bean中的驼峰,否则不知道该怎么进行映射,即使查询有值,也会因为无法映射二无法自动转化为bean -->
    <setting name="mapUnderscoreToCamelCase" value="true"/> 
</settings>
<!--指定对应的实体,进行别名映射-->
<typeAliases>
  <!--bb 为实体所在的包-->
	<package name="bb"></package>
</typeAliases>
<!--引入数据库连接参数,default指定引用哪个环境-->
<environments default="dev">
  <!--可根据不同的id 指定不同的环境-->
	<environment id = "dev">
      <!--事务管理器-->
      <transactionManager type="JDBC"></transactionManager>
      <!--指定对应的数据源,此处使用连接池-->
      <dataSource type="POOLED">
        <!--mybatis6以上 com.mysql.jdbc.Driver已经废弃,使用的新的driver class为com.mysql.cj.jdbc.Driver-->
        <property name="driver" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${username}"></property>
        <property name="password" value="${password}"></property>
      </dataSource>
  	</environment>
</environments>

<!--进行映射器配置 mapping-->
<mappers>
  <!--cc即为对应的XXXMapper.xml文件,从resources下级目录开始写 / 分割-->
  <mapper resource="cc"/>
</mappers>
2.2 XXXMapper.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-->
<mapper namespace="xx.xx.XXXMapper">
  <!-- 定义这个可以在此Mapper中进行引用,使用include标签,可以简化select书写-->
  <sql id = "allColumn">
    id,name,age
  </sql>
  <!-- parameterType为入参的类型,resultType为出参的类型,因为上面的配置文件已经配置了别名扫描,指定到vo包,而User.java在vo包中,所以此处可以直接使用别名,也可以使用全路径名7777786-->
  <select id = "getUserById" parameterType="long" resultType = "user">
        select
            <!--refid指定到对应需要引用的id-->
            <include refid="allColumn"/>
        from
            tb_user
        where
            id = #{id}
  </select>
</mapper>

3.指定了xxxMapper.xml之后只需要给定对应的接口就能自动进行映射

public interface UserMapper {
    //方法名即为对应的xml的id @Param参数对应#{}中的内容,返回值为resultType ,Long id 即为parameterType

    User getUserById(@Param("id") Long id);

}

这样就完成了对应关系的映射

4.使用:

        思路:首先新建一个sqlSession,接着使用sqlSession.getMapper()获取到对应的Mapper的对象,调用对应的方法即可

        实现:考虑到操作,所以使用一个工具类来创建sqlSession,代码如下:

public class SqlSessionFactoryUtil {

    private static SqlSessionFactory sqlSessionFactory;

    public static SqlSession openSqlSession() {
        if (sqlSessionFactory == null) {
            init();
        }
        // 设置自动提交
        return sqlSessionFactory.openSession(true);
    }

    private static SqlSessionFactory init() {
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }

        //加锁是你为了考虑高并发
        synchronized(SqlSessionFactoryUtil.class) {
            //此处再次进行判断,是高并发懒加载的情况下确保单例
            if (sqlSessionFactory == null) {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
            return sqlSessionFactory;
        }
    }
}

二、增删改查:

(一) 查询

1.使用自定义映射

<!-- 自定义映射,可使用id在resultmap参数中引用,类型即为自定义类型,主要使用场景:表字段很多,但是只需要一部分字段,此时使用全表映射很耗费资源,使用自定义映射,type中指定的就是自定义的bean,column 即为表中字段名称,property为bean中的字段-->
<resultMap id="messageVoReusltMap" type="vo.MessageVO">
        <id column="id" property="idVo"/>
        <result column="msg_id" property="msgIdVo"/>
        <result column="status" property="statusVo"/>
        <result column="content" property="contentVo"/>
        <result column="deleted" property="deletedVo"/>
        <result column="create_time" property="createTimeVo"/>
        <result column="update_time" property="updateTimeVo"/>
</resultMap>

<!--使用示例:-->
<select id="getMessageVOById" parameterType="long" resultMap="messageVoReusltMap">
        select
            <include refid="allColumns"/>
        from
            tb_message
        where
            id = #{id}
</select>


<!--接口-->
MessageVO getMessageVOById(Long id);

2.使用注解进行参数绑定

    // 指定多个注解参数映射
    <select id="getMessageByMap" parameterType="map" resultType="message">
        select
            <include refid="allColumns"/>
        from
            tb_message
        where
            id = #{id} and msg_id = #{msgId}
    </select>

<!--接口-->
    Message getMessageByIdAndMsgId(@Param("id") Long id, @Param("msgId") String msgId);

3.使用map进行绑定

    <select id="getMessageByMap" parameterType="map" resultType="message">
        select
            <include refid="allColumns"/>
        from
            tb_message
        where
            id = #{id} and msg_id = #{msgId}
    </select>

<!-- 接口-->
    Message getMessageByMap(Map<String, Object> params);

<!-- 调用处需要注意的是key需要与#{}中一一对应-->
Map<String, Object> params = new HashMap<String, Object>();
params.put("id", 1L);
params.put("msgId", "msg_1");
Message message = messageMapper.getMessageByMap(params);

4.使用实体进行传递

   <select id="getMessageByMessage" parameterType="vo.Message" resultType="message">
        select
            <include refid="allColumns"/>
        from
            tb_message
        where
            id = #{id} and msg_id = #{msgId}
    </select>

<!-- 接口-->
    Message getMessageByMessage(Message message);

5.使用map接收返回的结果

    <select id="getMessageMapById" parameterType="long" resultType="map">
        select
            <include refid="allColumns"/>
        from
            tb_message
        where
            id = #{id}
    </select>

<!--接口-->
    Map getMessageMapById(@Param("id") Long id);

注:map中拿到的内容是:key为表字段而不是对应的pojo,value是对应的值

(二)insert

1.insert方式(ID回显使用useGeneratedKeys)

    <!--设置useGeneratedKeys为true即可将插入的主键进行回显,另,需要注意的是如果不在sqlsession中设置autocommit为true,那么就需要在调用处,手工给定sqlSession.commit();-->
<insert id="insert" parameterType="message" keyProperty="id" useGeneratedKeys="true">
        insert into tb_message(<include refid="insertAllColumns"/>) values(#{msgId}, #{status}, #{content},
        #{deleted}, #{createTime})
    </insert>

<!-- 接口 -->
    int insert(Message message);

<!--调用-->
Message message = new Message();
message.setMsgId("msg_5");
message.setDeleted(0);
message.setStatus(1);
message.setContent("dddd");
message.setCreateTime(new Date());
messageMapper.insert(message);
// 手工提交,否则会被自动回滚
// sqlSession.commit();  
// 设置了useGeneratedKeys为true之后,会自动把生成的id放回原对象中,所以会在message中打印
System.out.println("message = " + message);

(三)update

  <update id="updateContentById">
        update tb_message set content=#{content} where id = #{id}
  </update>

<!--接口-->
    int updateContentById(@Param("id") Long id, @Param("content") String content);

(四)delete

    <delete id="deleteById" parameterType="long">
        delete from tb_message where id = #{id}
    </delete>

<!--接口-->
    int deleteById(Long id);

三、注意事项:

1.#与$的区别

$ 采用值传递的方式构建SQL语句        

# 采用预编译的方式构建SQL语句

在组建sql语句的过程中,$会将传入的参数直接拼接在 = 后面,有sql注入的风险:如

    <select id="getMessageByStringId" resultType="message">
        select
            <include refid="allColumns"/>
        from
            tb_message
        where
            id = #{id}
    </select>

    List<Message> getMessageByStringId(@Param("id") String id);

如果调用处传入的参数为:
List<Message> message = messageMapper.getMessageByStringId("xxx or 1=1");

此时,会查询表中所有的记录,造成数据泄露,xxx处可填任意值

所以一般都使用#进行预编译,拼接的sql语句为:

 四、级联操作

注:如果需要性能比较高的情况下,不要使用级联

1.一对一关系:association

示例:

public class Message {

    private Long id;
    private String msgId;
    private Integer status; // 消息状态,-1-待发送,0-发送中,1-发送失败 2-已发送
    private String content; // 消息内容
    private Integer deleted;
    private Date createTime;
    private Date updateTime;
    private MessageDetail messageDetail;
}

public class MessageDetail {
    private Long id;
    private String msgId;
    private String detailContent;
    private Date createTime;
    private Date updateTime;
}

如何在一次查询里面查询出这两个表中的数据,并且赋值给message,需要使用级联的方式

<!--接口-->
    Message getMessageAndMessageDetailById(@Param("id") Long id);

<!--查询语句与原先一致,不过此时返回的参数需要自定义-->
   <select id="getMessageAndMessageDetailById" parameterType="long" resultMap="messageAndDetailReusltMap">
        select
            <include refid="allColumns"/>
        from
            tb_message
        where
            id = #{id}
    </select>

    <resultMap id="messageAndDetailReusltMap" type="vo.Message">
        <id column="id" property="id"/>
        <result column="msg_id" property="msgId"/>
        <result column="status" property="status"/>
        <result column="content" property="content"/>
        <result column="deleted" property="deleted"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
           <!--association 标签就是指定一对一的级联,此处的property即在message中定义的messageDetail类,column指定查询messageDetail时使用哪一列,select指定使用哪个接口进行查询,此处使用类的全路径名-->
        <association property="messageDetail" column="msg_id"
                     select="mybatis.mapper.MessageDetailMapper.getMessageByMsgId" />
    </resultMap>


在mybatis.mapper.MessageDetailMapper中需要有如下接口:
    MessageDetail getMessageByMsgId(@Param("msgId") String msgId);

    <select id="getMessageByMsgId" parameterType="string" resultType="vo.MessageDetail">
        select
            <include refid="allColumns"/>
        from
            tb_message_detail
        where
            msg_id = #{msgId}
    </select>

就完成了一对一的级联操作

 2.一对多关系 collection

public class User implements Serializable {
    private Long id;
    private String name;
    private Integer age;
    private List<UserContact> userContacts;
}


public class UserContact {
    private Long id;
    private Long userId;
    private Integer contactType;
    private String contactValue;
    private Date createTime;
    private Date updateTime;
}
User getUserAndUserContactById(@Param("id") Long id);

    <select id="getUserAndUserContactById" parameterType="long" resultMap="userReuslt">
        select
            <include refid="allColumn"/>
        from
            tb_user
        where
            id = #{id}
    </select>

<!--因为是一对多的关系,所以此处需要使用collection,property指向pojo中定义的名称,column就是id = #{id} 中的id,select指向的getUserContactByUserId在UserContactMapper中-->
    <resultMap id="userReuslt" type="vo.User">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <collection property="userContacts" column="id"
                    select="mybatis.mapper.UserContactMapper.getUserContactByUserId" />
    </resultMap>


    List<UserContact> getUserContactByUserId(@Param("userId") Long userId);

    <select id="getUserContactByUserId" parameterType="long" resultType="vo.UserContact">
        select
            <include refid="allColumn"/>
        from
            tb_user_contact
        where
            user_id=#{userId}
    </select>

五、缓存

1.一级缓存

  •  mysql默认开启
  •  只针对一个sqlsession重复查询时使用一级缓存,不同的sqlsession一级缓存会失效

2.二级缓存

        开启二级缓存只需要在xxxMapper.xml中使用:

<mapper namespace="mybatis.mapper.UserMapper">
    <!--开启二级缓存-->
    <cache/>
</mapper>

注:要实现二级缓存需要在pojo中实现Serializable接口,否则会报错

 另,需要在第一次调用完之后,手动commit,否则二级缓存也不会生效,设置的自动提交是update、insert及delete的提交,select的提交需要手动执行。

代码如下:

public static void main(String[] args) {
        SqlSession sqlSession = null;
        try {
            sqlSession = SqlSessionFactoryUtil.openSqlSession();
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user1 = userMapper.getUserById(2L);
            System.out.println("user1 = " + user1);
            sqlSession.commit();
            User user2 = userMapper.getUserById(2L);
            System.out.println("user2 = " + user2);
            // 开启新的sqlSession
            sqlSession = SqlSessionFactoryUtil.openSqlSession();
            userMapper = sqlSession.getMapper(UserMapper.class);
            User user3 = userMapper.getUserById(2L);
            System.out.println("user3 = " + user3);
        } catch (Throwable e) {
            System.out.println("error!" + e.getMessage());
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
}

此时,user2和user3会使用二级缓存,且撞击率分别是 0.5和0.666666

3.自定义缓存

        实现mybatis的cache类,然后在开启二级缓存的cache标签中加上type="xxx.xxxCache"

<mapper namespace="mybatis.mapper.UserMapper">
    <!--开启自定义缓存,xxxCache需要实现mybatis的Cache类,然后自定义缓存实现,替换原有的二级缓存-->
    <cache type="com.xxx.xxxCache"/>
</mapper>

六、动态sql

1.if:使用test校验

 <select id="getUserByUser" parameterType="vo.User" resultMap="userResultMap">
        select id, name, age
        from tb_user
        where 1=1
        <if test="id != null">
            and id = #{id}
        </if>
        <if test="name != null and name != ''">
            and name = #{name}
        </if>
        <if test="age != null">
            and age = #{age}
        </if>
    </select>

2.choose when otherwise

    <select id="getUserByUser2" parameterType="vo.User" resultMap="userResultMap">
        select id, name, age
        from tb_user
        where 1=1
        <choose>
            <when test="id != null">
                and id = #{id}
            </when>
            <when test="name != null and name != ''">
                and name = #{name}
            </when>
            <otherwise>
                and age is not null
            </otherwise>
        </choose>
    </select>

3.where

<select id="getUserByUser3" parameterType="vo.User" resultMap="userResultMap">
        select
        id, name, age from tb_user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
        </where>
    </select>

4.trim

    <select id="getUserByUser4" parameterType="vo.User" resultMap="userResultMap">
        select
        id, name, age from tb_user
        /*prefix:前缀  prefixoverride:去掉第一个and或者是or*/
        <trim prefix="where" prefixOverrides="and">
            and id = #{id}
        </trim>
    </select>

    <select id="getUserByUser5" parameterType="vo.User" resultMap="userResultMap">
        select
        id, name, age from tb_user where 1 = 1 
        /*suffix:后缀  suffixOverrides:去掉最后一个,  用法与上面的prefixoverride一样*/
        <trim suffixOverrides="," suffix=" and id = #{id} " >
            <if test="msgId != null"> and msd_id=#{msgId} </if>
        </trim>
    </select>

5.set

    <update id="updateUserByUser" parameterType="vo.User">
        update tb_user
        <set>
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="age != null">
                age = #{age},
            </if>
        </set>
        where id = #{id}
    </update>

6.foreach

    <select id="getUserByIds" resultMap="userResultMap">
        select
        id, name, age
        from tb_user
        where id in
        /*collection是传入的集合,index 用来指定名字 ,用来表示在迭代过程中,每次迭代到的位置,一般用不到,open为开头,close为结尾,separator为分隔符*/
        <foreach collection="idList" index="index" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>

7.bind

    <select id="getUserByName" parameterType="string" resultMap="userResultMap">
        <bind name="namePattern" value="'%' + name + '%'"/>
        select
        id, name, age
        from
        tb_user
        where
        name like #{namePattern}
        <!-- 类似于name like concat('%', #{name}, '%') -->
    </select>

**************此文章只是本人学习过程中的学习笔记,不做其他用途,如果有其他意见,欢迎一起讨论,谢谢,侵删*************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值