文章目录
创建第一个mybatis程序
-
准备数据库环境
-
搭建普通maven项目环境
-
倒入maven依赖
-
删除src目录(为了新建后面的子模块)
-
在子模块中编写mybatis的核心配置文件,这个框架需要配置之后才能使用
<?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"> <configuration> <!-- mybatis核心配置文件--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration>
-
编写mybatis工具类,因为需要从配置文件中拿到建造者来建造工厂,读取配置文件这个过程可以写成工具类
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { String resource = "mybatis-config.xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } }
-
开始写程序(就按照mvc三层架构开始)
-
dao层接口
package com.zhong.dao; import com.zhong.pojo.User; import java.util.List; public interface UserDao { public List<User> getUserList(); }
-
dao层实现类(这里是配置文件来实现,由原来的impl转换成mapper.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 namespace="com.zhong.dao.UserDao"> <select id="getUserList" resultType="com.zhong.pojo.User"> select * from `user`; </select> </mapper>
-
测试问题。因为我们的mapper.xml不是建在resources文件夹下的,所以需要对mapper.xml进行注册。但是由于maven无法导出Java文件夹下的配置文件。所以需要在xml中配置过滤,让maven能够导出我们的mapper.xml。或者把mapper.xml直接建立在resources文件夹下,然后在配置中文件路径写文件名即可
-
Map
使用map键值对来传递参数
配置解析
核心配置
环境配置
学会配置多套环境
属性
可以通过properties来动态的配置mybatis的核心配置文件。通过在核心配置文件中的properties的属性来引用
别名
在配置文件中,使用完全限定名规定类型,我们可以使用别名来为减少完全限定名的使用
<!-- 给实体类起别名-->
<typeAliases>
<typeAlias type="com.zhong.pojo.User" alias="User"></typeAlias>
</typeAliases>
包扫描,会默认的给包中的实体类起一个首字母小写的类名的别名
<!-- 给实体类起别名-->
<typeAliases>
<package name="com.zhong.pojo"/>
</typeAliases>
起别名也可以直接在实体类里面实现,在实体类中使用注解@Alias(“别名”)。在所有的起别名的方式中,注解的优先级最高
设置
映射器
注册mapper,就像使用配置文件去实现接口必须要注册一样。它有三种方式resouce使用/来分隔。class使用.分隔。以及打包一个包内的mapper package name=…
第二第三种方式需要注意的是,配置文件必须和接口同名,而且配置文件必须和接口在同一个包下
生命周期和作用域
错误的使用生命周期和作用域会导致严重的并发问题
ResultMap
用来解决数据库字段名和封装的对象的属性名不一致的问题,当这两个不一致的时候,mybatis无法将数据库查出来的数据放到对应的位置上
<resultMap id="UserMap" type="User">
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<result column="pwd" property="password"></result>
</resultMap>
<select id="getUserById" parameterType="int" resultMap="UserMap">
select * from `user` where id = #{id};
</select>
日志
日志工厂
STDOUT_LOGGING 标准日志输出,开箱即用
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
LOG4J 需要配置(IDEA 2022.1弃用log4j)
导包
<!-- 日志导包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
log4j配置文件
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
使用
static Logger logger = Logger.getLogger(UserDaoTest.class);
@Test
public void testLog4j() {
logger.info("info");
logger.debug("debug");
logger.error("error");
}
分页
List<User> getUserByLimit(Map<String,Object> map);
<select id="getUserByLimit" resultType="User" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize};
</select>
@Test
public void testLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
}
使用注解开发
当我们需要使用一些简单的sql语句,可以使用注解来代替配置xml文件。这会让配置文件看起来比较干净整洁。但是使用一些复杂的sql语句的时候,在使用xml配置文件还是很有必要的。比如数据库字段和实体类数据名字不一样这样就映射不了
直接在接口的方法上面写@select("这里写你的查询sql")
即可
@Select("select * from `user`")
List<User> getUsers();
<mappers>
<!--<mapper resource="com/zhong/dao/UserMapper.xml"></mapper>-->
<mapper class="com.zhong.dao.UserMapper"></mapper>
</mappers>
参数注解
在使用注解写sql语句的时候,如果方法本身需要一个外部的参数,那么在sql语句中为了使用到这个参数,需要在参数列表的参数前面加上参数注解@param("被sql调用的参数名")
。参数注解只使用在基本类型和String,引用类型不需要参数注解
Lombok使用(重要):
这是一个java的库,它是一个插件、构建工具。使用注释就可以写getter等方法。可以用idea的插件市场安装
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
常用的注解
@Data//getter...等方法
@AllArgsConstructor//所有参数的构造方法
@NoArgsConstructor//无参数构造方法
@ToString
多对一的处理
我们需要在实现xml文件中关联sql语句,并且做结果集映射
按照查询嵌套处理
<resultMap id="StudentTeacher" type="Student">
<!--简单映射可以直接使用result,但是对于复杂的对象需要进行特殊的处理-->
<!--对象:association(多对一)
集合:collection(一对多)-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"></association>
</resultMap>
<select id="getStudent" resultMap="StudentTeacher">
select * from mybatis.student;
</select>
<select id="getTeacher" resultType="Teacher">
select * from mybatis.teacher where id = #{id};
</select>
按结果嵌套查询
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid, s.name sname, t.name tname
from mybatis.student s,mybatis.teacher t
where s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"></result>
</association>
</resultMap>
一对多处理
按结果嵌套查询
<select id="getTeacher" resultMap="TeacherStudent">
select s.name sname, s.id sid, t.name tname, t.id tid
from mybatis.teacher t, mybatis.student s
where t.id = s.tid and t.id = #{tid};
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"></result>
<result property="name" column="tname"></result>
<!--这里clooection要区别javatype,因为是集合所以用oftype-->
<collection property="students" ofType="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="tId" column="tid"></result>
</collection>
</resultMap>
按查询嵌套查询
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from mybatis.teacher where id = #{tid};
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacher"></collection>
</resultMap>
<select id="getStudentByTeacher" resultType="Student">
select * from mybatis.student where tid = #{tid};
</select>
动态sql
动态sql和jstl标签类似。主要有这几种
- if
- choose(when,otherwise)
- trim(where,set)
- foreach
if
@Test
public void test1() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String,String> map = new HashMap<String,String>();
map.put("title","Java");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
//查询博客
List<Blog> queryBlogIF(Map map);
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
choose(when,otherwise)
在我们使用if标签来拼接sql语句的时候,我们常常使用where 1=1来处理每个语句前面跟着的and的问题。但是这个1=1实际上是没有任何意义的,它只是为了帮助我们去处理掉where后面直接跟着and或者or的问题。使用where标签可以帮助我们去掉1=1这样没有意义的sql语句。
where便签里面正常写if标签的内容即可,只要有一个if成立,那么where语句会自动的插入where,不需要我们写。并且如果where后面紧跟了and或者or这样的关键字,where标签也会帮我们把and或者or去掉
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
choose
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog where
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
author = #{author}
</when>
<otherwise>
views = #{views}
</otherwise>
</choose>
</select>
trim(where,set)
set标签会动态的加上set关键字,并且会会帮助我们去掉多余的逗号。在set里写if标签动态的添加sql语句
<update id="updateBlog" parameterType="map">
update `mybatis`.blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author},
</if>
</set>
where id = #{id}
</update>
where和set都有一个父类
<trim prefix=" " prefixOverrides="" suffix="" suffixOverrides=""></trim>
我们可以使用这个父类来定制我们需要的类似set或者where的功能
prefix:需要时自动加上前缀
prefixOverrides:需要时,自动忽略语句前的东西。例如and或者or使用 | 分隔
suffix:需要时自动加上后缀
suffixOverrides:需要时,自动忽略语句后的东西。例如set后面的逗号
SQL片段
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
这样的叫做sql片段,在sql语句中使用include包含
<select id="queryBlogWhere" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
Foreach
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" separator="or" close=")" open="(">
id = #{id}
</foreach>
</where>
</select>
@Test
public void test3() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
缓存
存储在内存中的临时数据
如果把我们一次查询到的数据放在内存里面,以后需要的时候去内存调用,这样就可以不用查询数据库了,查询数据库需要IO,IO相对于内存是很慢的。缓存的出现解决了高并发系统的性能问题
经常查询且不经常改变的数据可以使用缓存
mybatis默认有一级缓存和二级缓存
- 默认一级缓存开启,一级缓存就是sqlsession的缓存
- 二级缓存需要手动开启或者配置,它是基于namespace级别的缓存
- 还有接口缓存cache。我们可以通过实现cache接口来自定义二级缓存
一级缓存
缓存失效
- 增删改会刷新缓存
- 查询不同的mapper.xml
- 手动清除缓存
二级缓存
启用二级缓存需要在sql映射文件中添加<cache/>
并且要在核心配置文件的setting中开启缓存<setting name="cacheEnabled" value="true"></setting>
二级缓存使用新会话查询的时候,需要实体类序列化,如果实体类没有序列化,那么cache标签需要设置属性readOnly为true
所有缓存数据都会放在一级缓存中,只有当会话关闭或者提交的时候才会提交到二级缓存
缓存原理
自定义缓存-ehcache
要使用它,需要去导包mybatis-ehcache
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
实际工作使用redis更多