官方文档:https://mybatis.org/mybatis-3/zh/
mybatis CRUD
获取保存数据的id
- 使用了selectKey标签。可以获取数据库自增赋予的id值。
- 本质上,还是sql语句的查询:
select last_insert_id();
mapper映射配置文件写法
<insert id="saveUser" parameterType="com.wei.pojo.User">
<selectKey keyProperty="id" resultType="int" keyColumn="id" order="AFTER" >
select last_insert_id();
</selectKey>
insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
public void fun2(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
User user = new User();
user.setAddress("福建");
user.setBirthday(new Date());user.setSex("女");user.setUsername("ms林");
System.out.println("保存之前"+user);
mapper.saveUser(user);
System.out.println("保存之后"+user);
sqlsession.commit();
}
log日志 和 输出结果
User(id=null, username=ms林, birthday=Thu Jul 23 22:46:32 CST 2020, sex=女, address=福建仙游)
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 712025048.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2a70a3d8]
[com.wei.mapper.UserMapper.saveUser]-==> Preparing: insert into user values(?,?,?,?,?)
[com.wei.mapper.UserMapper.saveUser]-==> Parameters: null, ms林(String), 2020-07-23 22:46:32.334(Timestamp), 女(String), 福建仙游(String)
[com.wei.mapper.UserMapper.saveUser]-<== Updates: 1
[com.wei.mapper.UserMapper.saveUser!selectKey]-==> Preparing: select last_insert_id();
[com.wei.mapper.UserMapper.saveUser!selectKey]-==> Parameters:
[com.wei.mapper.UserMapper.saveUser!selectKey]-<== Total: 1
User(id=55, username=ms林, birthday=Thu Jul 23 22:46:32 CST 2020, sex=女, address=福建仙游)
OGNL表达式写法 Object Graphic Navigation Language
- 它是通过对象的取值方法来获取数据,在写法上省略了get。
- 比如讲user.getUsername() 写为 user.username
- mybatis中可以直接写的原因是因为在parameterType中已经提供了属性所属的类,所以不需要写对象名。
@Data
public class QueryVO {
private User user;
}
///测试类
@Test
public void findByQueryVO(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
UserMapper mapper = sqlsession.getMapper(UserMapper.class);
QueryVO qv = new QueryVO();
User user = new User();
user.setUsername("%王%");
qv.setUser(user);
List<User> byQueryVO = mapper.findByQueryVO(qv);
System.out.println(byQueryVO);
}
<select id="findByQueryVO" resultType="com.wei.pojo.User" parameterType="com.wei.pojo.QueryVO">
select * from user where username like #{user.username}
</select>
数据库字段与实体类属性不一致的解决方法
- sql语句中给字段取别名。在sql语句的层面上解决问题,效率也是最高的。
- 配置查询结果的列名和实体类的属性名的对应关系。ResultMap。
配合查询标签的resultmap属性,需要指定ResultMap标签的id
<resultMap id="usermapper" type="com.wei.pojo.User">
<id property="实体类属性" column="数据库字段" />
...
</resultMap>
主配置文件下的几个标签
- 主配置文件下< configuration> 标签的子标签的顺序。
- 元素类型为 “configuration” 的内容必须匹配 “(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)”。
properties
- 可以再标签内部配置连接数据库的信息。也可以通过外部配置文件。
- 需要配置jdbcConfig.properties文件使用的。
- resource属性用于指定配置文件的位置,并且必须存在于类路径下。
- url 属性 可以唯一标识一个资源的位置。
<properties resource="jdbcConfig.properties">
</properties>
- jdbcConfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
- 使用时应用datasource引用。
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
setting
setting参考链接:https://blog.csdn.net/w495837651/article/details/78004916
自己blog:https://blog.csdn.net/qq_30081043/article/details/107578396
<!-- settings是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 -->
<settings>
<!-- 该配置影响的所有映射器中配置的缓存的全局开关。默认值true -->
<setting name="cacheEnabled" value="true"/>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认值false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 是否允许单一语句返回多结果集(需要兼容驱动)。 默认值true -->
<setting name="multipleResultSetsEnabled" value="true"/>
<!-- 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。默认值true -->
<setting name="useColumnLabel" value="true"/>
<!-- 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 默认值false -->
<setting name="useGeneratedKeys" value="false"/>
<!-- 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 -->
<!-- 默认值PARTIAL -->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<!-- 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。默认SIMPLE -->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!-- 设置超时时间,它决定驱动等待数据库响应的秒数。 -->
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<!-- 允许在嵌套语句中使用分页(RowBounds)默认值False -->
<setting name="safeRowBoundsEnabled" value="false"/>
<!-- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 默认false -->
<setting name="mapUnderscoreToCamelCase" value="false"/>
<!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。 -->
<setting name="localCacheScope" value="SESSION"/>
<!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 -->
<setting name="jdbcTypeForNull" value="OTHER"/>
<!-- 指定哪个对象的方法触发一次延迟加载。 -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
typeAlias 类型别名
- 配置了,就可以在resultType(返回值)上直接写别名,而不用写全限定类名
- 这也是一些内置的数据类型可以简写的原因。
- 第一种略写的方式
- type 指定 全限定类名
- alias 指定 别名 ,指定之后,便不区分大小写。
<typeAliases>
<typeAlias type="com.wei.pojo.User" alias="user" />
...
</typeAliases>
- 第二种略写的方式
- 用于指定要配置别名的包,指定之后,该包下实体类都会注册别名
- 并且类名就是别名,不再区分大小写。
<typeAliases>
<package name="com.wei.pojo"/>
</typeAliases>
dataSource 类型
- POOLED
- 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现。
- 使用完归还给连接池。
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 315138752.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@12c8a2c0]
[com.wei.mapper.UserMapper.findAll]-==> Preparing: select * from user
[com.wei.mapper.UserMapper.findAll]-==> Parameters:
[com.wei.mapper.UserMapper.findAll]-<== Total: 11
[User(id=41, username=老王, birthday=Tue Feb 27 17:47:08 CST 2018, sex=男, address=北京), User(id=42, username=小二王, birthday=Fri Mar 02 15:09:37 CST 2018, sex=女, address=北京金燕龙), User(id=43, username=小二王, birthday=Sun Mar 04 11:34:34 CST 2018, sex=女, address=北京金燕龙), User(id=45, username=传智播客, birthday=Sun Mar 04 12:04:06 CST 2018, sex=男, address=北京金燕龙), User(id=46, username=老王, birthday=Wed Mar 07 17:37:26 CST 2018, sex=男, address=北京), User(id=48, username=小马宝莉, birthday=Thu Mar 08 11:44:00 CST 2018, sex=女, address=北京修正), User(id=50, username=王, birthday=Thu Jul 23 21:06:05 CST 2020, sex=男, address=福建), User(id=52, username=ms林, birthday=Thu Jul 23 22:42:31 CST 2020, sex=女, address=福建仙游), User(id=53, username=ms林, birthday=Thu Jul 23 22:43:49 CST 2020, sex=女, address=福建仙游), User(id=54, username=ms林, birthday=Thu Jul 23 22:44:10 CST 2020, sex=女, address=福建仙游), User(id=55, username=ms林, birthday=Thu Jul 23 22:46:32 CST 2020, sex=女, address=福建仙游)]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@12c8a2c0]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@12c8a2c0]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 315138752 to pool.
Process finished with exit code 0
-
原理图源自网络。
-
UNPOOLED
-
采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
-
使用完关闭连接,不归还给池。
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@17d0685f]
[com.wei.mapper.UserMapper.findAll]-==> Preparing: select * from user
[com.wei.mapper.UserMapper.findAll]-==> Parameters:
[com.wei.mapper.UserMapper.findAll]-<== Total: 11
[User(id=41, username=老王, birthday=Tue Feb 27 17:47:08 CST 2018, sex=男, address=北京), User(id=42, username=小二王, birthday=Fri Mar 02 15:09:37 CST 2018, sex=女, address=北京金燕龙), User(id=43, username=小二王, birthday=Sun Mar 04 11:34:34 CST 2018, sex=女, address=北京金燕龙), User(id=45, username=传智播客, birthday=Sun Mar 04 12:04:06 CST 2018, sex=男, address=北京金燕龙), User(id=46, username=老王, birthday=Wed Mar 07 17:37:26 CST 2018, sex=男, address=北京), User(id=48, username=小马宝莉, birthday=Thu Mar 08 11:44:00 CST 2018, sex=女, address=北京修正), User(id=50, username=王, birthday=Thu Jul 23 21:06:05 CST 2020, sex=男, address=福建), User(id=52, username=ms林, birthday=Thu Jul 23 22:42:31 CST 2020, sex=女, address=福建仙游), User(id=53, username=ms林, birthday=Thu Jul 23 22:43:49 CST 2020, sex=女, address=福建仙游), User(id=54, username=ms林, birthday=Thu Jul 23 22:44:10 CST 2020, sex=女, address=福建仙游), User(id=55, username=ms林, birthday=Thu Jul 23 22:46:32 CST 2020, sex=女, address=福建仙游)]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@17d0685f]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@17d0685f]
- JNDI
- 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。
动态sql的使用
- where & if
<select id="findBlog" resultType="com.wei.pojo.Blog">
select * from blog
<where>
<if test="author!= null">
author = #{author}
</if>
</where>
</select>
select * from blog
- 如果传入的author是null,则会忽略,变成了查询所有
- 如果传入的不为空,则会查询对应的author,如果不存在就是空集合。
- set & if
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id};
</update>
仅仅传递title
update blog SET title = ? where id = ?;
2个字段都不传递
update blog where id = ?;
- 动态生成update的sql,传递title和author的其中之一就可以修改,或者都传递。
- 如果二者都不传,则会出现错误。
- choose & when & otherwise
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
使用when标签里的属性的时候
select * from blog WHERE author = ? , title = ?
不使用when标签里的属性的时候使用,它会替我们去掉views 前的 and
select * from blog WHERE views = ?
- 可以选择title 或者 author 其中一个,或者全部进行查询 blog
- 如果两个都不选择,则会使用views来查询,在使用以上2个字段的情况下,不使用views
- sql & include 标签 sql代码复用
<select id="findBlog" resultType="com.wei.pojo.Blog">
<include refid="selectBlog"/>
<where>
<if test="author!= null">
author = #{author}
</if>
</where>
</select>
<sql id="selectBlog">
select * from blog
</sql>
select * from blog
select * from blog where author = ?
sql标签使用的注意事项:
- 最好基于单表来定义SQL片段,有利于复用。
- 不要存在where标签
foreach
Java测试
// mapper
public interface BlogMapper {
List<Blog> selectBlogForEach(Map map);
}
//测试
@Test
public void selectBlogForEach(){
SqlSession sqlsession = MybatisUtils.getSqlsession();
BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
HashMap hashMap = new HashMap();
ArrayList<Integer> ids = new ArrayList();
ids.add(1);ids.add(2);
hashMap.put("ids",ids);
List<Blog> blogs = mapper.selectBlogForEach(hashMap);
}
<select id="selectBlogForEach" parameterType="map" resultType="com.wei.pojo.Blog">
select * from blog
<where>
<foreach collection="ids" open=" id in (" close=")" separator="," item="id">
#{id}
</foreach>
</where>
</select>
对应的sql语句
select * from blog WHERE id in ( ? , ? )
懒加载(延迟加载)的使用
myblog https://blog.csdn.net/qq_30081043/article/details/107581869
mybatis 缓存
- 适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。 - 不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
- 经常改变的数据,不能用缓存。
一级缓存 sqlsession级别
缓存生效
@Test
public void fun1() {
SqlSession sqlSession = MybatisUtils.getSqlsession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog one = blogMapper.findOne(1);
Blog two = blogMapper.findOne(1);
System.out.println(one==two);//true,同一对象。
sqlSession.close();
}
缓存失效
1.调用sqlSession的增删改,commit(),close() 等方法会清空一级缓存
2.手动清除缓存、最近最少使用算法清除
3.sqlsession不同或sqlsession关闭后再次打开(同样是不同sqlsession)
4.不定时的刷新
@Test
public void fun2() {
SqlSession sqlSession = MybatisUtils.getSqlsession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Blog one = blogMapper.findOne(1);
sqlSession.close();//需要再次创建,但是结果还是false
sqlSession.clearCache();//清除了缓存,false
//增删改 //清除了缓存,false
System.out.println(one==two);//false
}
二级缓存 sqlsessionFactory级别
-
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
-
会话关闭了,对应的一级缓存就没了;会话关闭了,一级缓存中的数据被保存到二级缓存中;
-
二级缓存 存放的内容是数据,而不是对象。每次使用缓存都是拷贝而不是引用。
如何开启二级缓存
1、开启全局缓存【mybatis-config.xml】
其实默认为true,可配可不配。
<setting name="cacheEnabled" value="true"/>
2、每个mapper.xml中配置使用二级缓存;
- 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
<cache eviction="FIFO" flushInterval="200000" readOnly="true" size="512"/>
<cache/> <!-- 2种 配置方式都可以。-->
3、代码测试
@Test
public void fun10() {
SqlSession sqlSession = MybatisUtils.getSqlsession();
SqlSession sqlSession2 = MybatisUtils.getSqlsession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
BlogMapper blogMapper2 = sqlSession2.getMapper(BlogMapper.class);
Blog one = blogMapper.findOne(1);
sqlSession.close();//如果没有关闭sqlsession,则结果为false,因为二级缓存不存在
Blog two = blogMapper2.findOne(1);
System.out.println(one==two);//true,同一对象。
}
4、结论
- 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
- 查出的数据都会被默认先放在一级缓存中
- 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
- 获取数据的顺序。先看二级缓存有没有缓存,再看一级缓存有没有缓存,再查询数据库