Mybatis
1、mybatis是什么,为什么要用mybatis?
Mybatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statment、手动设置参数、结果集检索等jdbc繁琐的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过Java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成Java队形并返回。
Mybatis架构图
2、第一个Mybatis程序
执行步骤:
-
1、导入mybatis的相关jar包
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
-
2、编写核心配置文件mybatis-config.xml
<?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> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/hao/dao/UserMapper.xml"/> </mappers> </configuration>
-
3、编写实体类、接口、工具类
实体类:
package com.hao.pojo; public class User { private int id; private String myname; private int age; private String address; public User() { } public User(int id, String myname, int age, String address) { this.id = id; this.myname = myname; this.age = age; this.address = address; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMyname() { return myname; } public void setMyname(String myname) { this.myname = myname; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", myname='" + myname + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } }
接口:
package com.hao.dao; import com.hao.pojo.User; import java.util.List; public interface UserMapper { List<User> getUserList(); }
工具类:
package com.hao.utils; 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 { //获取sqlSessionFactory对象 try { String resource = "mybatis-config.xml"; InputStream inputStream =Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //获取SqlSession对象 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
4、编写实体类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.hao.dao.UserMapper"> <select id="getUserList" resultType="com.hao.pojo.User"> select * from users </select> </mapper>
-
5、测试
package com.hao.dao;
import com.hao.pojo.User;
import com.hao.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MybatisTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
3、配置解析
核心配置文件mybatis-config.xml中的配置的设置和属性
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) type="[JDBC|MANAGED]" dataSource(数据源) type="[UNPOOLED|POOLED|JNDI]" databaseIdProvider(数据库厂商标识) mappers(映射器)
3.1 配置环境(environments)
Mybatis可以配置成适应多种环境,不过要记住:尽管可以配置多个环境,但每个sqlsessionFactory实例只能选择一种环境。
Mybatis默认的事务管理器就是JDBC,链接池:pooled
3.2属性(properties)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
3.3类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
当这样配置时,Blog
可以用在任何使用 domain.blog.Blog
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
3.4映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///
形式的 URL),或类名和包名等。例如:
方式一:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
方式二:
注意点:
- 接口和他的mapper配置文件必须同名
- 接口和他的mapper配置文件必须在同一包下
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
方式三:
注意点:
- 接口和他的mapper配置文件必须同名
- 接口和他的mapper配置文件必须在同一包下
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
3.5解决属性名和字段名不一致的问题(ResultMap结果集映射)
问题:实体类的属性名和数据库表中的字段名不一样?该怎么解决呢?
解决方法:
- 起别名:
<select id="getuser" resultType="User">
select id,name,pwd as password from user where id=#{id}
</select>
- ResultMap结果集映射
<select id="getuser" resultMap="UserMap">
select * from user where id=#{id}
</select>
<resultMap id="UserMap" type="User">
<!--column为数据库中字段名,property为实体类中的属性-->
<result column="id" property="id"/>
<result column="name" property="name"/> <!--如果字段名和属性名一样,映射可有可无-->
<result column="pwd" property="password"/>
</resultMap>
4、日志
如果一个数据库操作出现了异常,我们需要排查,日志就是最好的助手。
在mybatis-config.xml核心配置文件中配置settings,添加日志即可
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
4.1Log4j
什么是Log4j?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
5、分页
使用limit分页
语法:select * from user limit startIndex,pagesize;
mybatis中是通过mapper接口方式的调用sql层面实现分页
#{} 和${}的区别
-
{}为占位符,即sql预编译,#{} 的变量替换是在DBMS 中,变量替换后,#{} 对应的变量自动加上单引号 ',#{} 能防止sql 注入
-
${}为拼接符,即sql拼接,${} 的变量替换是在 DBMS 外,变量替换后,${} 对应的变量不会加上单引号 '',${} 不能防止sql 注入
6、多对一、一对多
6.1 多对一处理
示例:多个学生关联一个老师
方式一:
实体类
package com.hao.pojo;
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
package com.hao.pojo;
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
按照查询嵌套处理
<!--思路:
1.查询所有学生信息
2.根据查询出来的tid,找到对应的老师信息,子查询
-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="com.hao.pojo.Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="com.hao.pojo.Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="com.hao.pojo.Teacher">
select * from teacher where id =#{id}
</select>
方式二:
按照结果嵌套处理
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname,t.id tidd
from student s,teacher t
where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="com.hao.pojo.Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="com.hao.pojo.Teacher">
<result property="name" column="tname"/>
<result property="id" column="tidd"/>
</association>
</resultMap>
6.2一对多处理
一个老师教多个学生
实体类:
package com.hao.pojo;
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
private int tid;
}
package com.hao.pojo;
import lombok.Data;
import java.util.List;
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
方式一:按照子查询嵌套处理
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id=#{id}
</select>
<resultMap id="TeacherStudent2" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudent"/>
</resultMap>
<select id="getStudent" resultType="student">
select * from student where tid=#{id}
</select>
方式二:按照结果集嵌套处理
<select id="getTeacher" resultMap="TeacherStudent">
select t.id tid,t.name tname,s.id sid,s.name sname,s.tid stid
from teacher t ,student s
where t.id=s.tid and t.id=#{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="stid"/>
</collection>
</resultMap>
个人推荐使用第二种,方便、快捷,一步到位。
小结
1、关联-association [多对一]
2、集合-collection [一对多]
3、javaType、ofType
- javaType 用来指定实体类种属性的类型
- ofType 用来指定映射到List或者集合中的实体类性,泛型种的约束类型。
7、动态SQL
什么是动态sql:动态sql就是根据不同的条件生成不同的sql语句
if
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
trim(where,set)
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
choose(when,otherwise)
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
sql片段
有些时候,我们可能会将一些功能部分抽取出来,方便复用
1、使用sql标签抽取公共部分
2、在需要使用的地方使用include标签即可。
注意事项:
- 最好基于单表定义sql片段
- 不要存在where标签
forEach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
8、缓存
8.1什么是缓存
1、什么是缓存【Cache】?
- 存在内存种的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2、为什么使用缓存?
- 减少和数据库的交互次数,减少系统的开销,提高系统的效率
3、什么样的数据能使用缓存
- 经常查询且不经常改变的数据
8.2Mybatis缓存
- Mybatis包含了一个非常强大的查询缓存特征,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(Sqlsession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
- 为了提高扩展性,Mybatis定义了接口Cache。我们可以通过实现Cache接口来自定义二级缓存
8.3一级缓存
- 一级缓存也叫本地缓存:Sqlsession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
- 缓存失效的情况:
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存。
- 查询不同的mapper.xml文件
- 手动清理缓存 sqlsession.clearCache();
小结:一级缓存是默认开启的,只在一次Sqlsession中有效,也就是拿到连接到关闭连接的这个区间!
8.4二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。
- 基于namaspace级别的缓存,一个名称空间,对应的一个二级缓存。
- 工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据就会被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存中;
步骤:
1、开启全局缓存(cacheEnabled默认的值为true)
<setting name="cacheEnabled" value="true"/>
2、在要使用二级缓存中的Mapper中开启
<cache/>
也可以自定义参数
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
问题:我们需要将实体类序列化,实体类需要实现Serializable接口
小结:
- 只要开启了二级缓存,在同一个mapper中就有效
- 所有的数据都会先放在一级缓存中;
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中。