一、基础部分
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>
**************此文章只是本人学习过程中的学习笔记,不做其他用途,如果有其他意见,欢迎一起讨论,谢谢,侵删*************************