官网:https://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
** 在pom.xml文件中**加载mysql驱动依赖、mybatis依赖和junit依赖****
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bjpowernode</groupId> <artifactId>LogMybatis</artifactId> <version>1.0-SNAPSHOT</version> <name>LogMybatis</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <!--mysqL驱动依赖版本不能过高--> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!--log4j依赖--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources> </build> </project>其中,JDBC注册驱动按下面的properties配置文件进行注册连接
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/bjpowernode?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root password=199478 书写pojo.user(数据库表)实例类、utils.util的JDBC工具类和dao.userdao(userMapper)接口,并配置实现方法的xml文件
a. 书写pojo.user数据库实例类(注意和数据库名字保持一致)
package com.bjpowernode.pojo; public class user { private int Id; private String name; private int age; public user() { } public user(int id, String name, int age) { Id = id; this.name = name; this.age = age; } public int getId() { return Id; } public void setId(int id) { Id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "user{" + "Id=" + Id + ", name='" + name + '\'' + ", age=" + age + '}'; } } b. 书写dao.userdao接口和实现xml配置文件
//dao.userdao接口 package com.bjpowernode.dao; import com.bjpowernode.pojo.user; import java.util.List; import java.util.Map; public interface UserDao { List<user> selectUser(); //插入数据 public int insertUser(user u); //根据id查找数据 public user getuserById(int id); //更新 public int updateById(user user); //删除数据 public int deleteById(int Id); //通过map方式操作数据库 public int addByMap(Map<String, Object> map); //根据Id实现分页查询 public List<user> selectByLimit(Map<String, Integer> map1); }
<?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.bjpowernode.dao.UserDao"> <!--借助与map实现limit分页查询--> <select id="selectByLimit" parameterType="map" resultType="User"> select * from user limit #{startIndex}, #{pageSize} </select> <!--普通查询--> <select id="selectUser" resultType="com.bjpowernode.pojo.user"> select * from user </select> <!--插入数据--> <insert id="insertUser" parameterType="com.bjpowernode.pojo.user"> insert into user (Id,name,age) values (#{Id},#{name}, #{age}) </insert> <!--插入map类型数据--> <insert id="addByMap" parameterType="map"> insert into user (Id,name,age) values (#{mapId},#{mapName},#{mapAge}) </insert> <!--获取数据--> <select id="getuserById" resultType="com.bjpowernode.pojo.user"> select * from user where Id=#{Id} </select> <!--根据id更新数据--> <update id="updateById" parameterType="com.bjpowernode.pojo.user"> update user set name = #{name}, age =#{age} where Id=#{Id} </update> <!--根据id删除数据--> <delete id="deleteById" > delete from user where Id=#{Id} </delete> </mapper> c. 书写util工具类,包装SqlSession实例对象
package com.bjpowernode.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 utils { static private SqlSessionFactory sqlSessionFactory; static { try { //这三句话是mybatis官网固定写法 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getsqlSession(){ return sqlSessionFactory.openSession(); } }解决字段名和属性名字不一致的情况
1)limit
2)RowBounds(了解)
根本原因:解耦和
关于接口的理解
接口从更深层次的理解,应该是定义(规范、约束)与实现(名、实分离的原则)的分离接口的本身反映了系统设计人员的抽象理解接口应该有两类: 第一类是对一个个体的抽象,即对应一个抽象体第二类是对一个个体某一方面的抽象,即形成一个抽象面 一个个体可能有多个抽象面,抽象体与抽象面是由区别的三个面向区别
面向对象是指,我们在考虑问题时,以对象为单位,考虑他的属性及方法面向过程是指,我们在考虑问题时,以一个具体的流程(事务过程)为单位,考虑他的实现接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题。更多的体现是对系统整体的架构。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ul9IOBS5-1601702455044)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201001180836065.png)]
设置自动提交
public static SqlSession getsqlSession(){ //true表示设置自动提交事务 return sqlSessionFactory.openSession(true); }使用注解实现增删改查
package com.bjpowernode.dao; import com.bjpowernode.pojo.user; import org.apache.ibatis.annotations.*; import java.util.List; import java.util.Map; public interface UserDao { @Select("select * from user") List<user> selectUser(); //根据id查询, 注意Param中的参数名字与#{Id}一致 @Select("select * from user where Id = #{userId}") user getUserById(@Param("userId") int id); //添加用户 @Insert("insert into user(Id, name, age) values (#{userId}, #{userName}, #{userAge})") int addUser(@Param("userId") int id, @Param("userName") String name, @Param("userAge") int age); //更新 @Update("update user set name=#{name}, age=#{age} where Id=#{Id}") int updateUser(user user); //删除 @Delete("delete from user where Id=#{userId}") int deleteUser(@Param("userId") int id); }编写测试类执行操作
@Test public void tse01(){ SqlSession sqlSession = null; try{ //获取SqlSession sqlSession = utils.getsqlSession(); //获取mapper,等价于dao。用于操作数据库的对象实例 UserDao mapper = sqlSession.getMapper(UserDao.class); //调用方法,进行查询操作 List<user> userList = mapper.selectUser(); for (user user : userList) { System.out.println(user); } }finally { //关闭资源,放在finally块中,防止忘记关闭 sqlSession.close(); } }注意:在mybatis-config中绑定接口
<mappers> <mapper class="com.bjpowernode.dao.UserDao"/> </mappers>关于@Param()注解
基本类型的参数或者String类型,需要加入,引用类型不需要加
//更新 @Update("update user set name=#{name}, age=#{age} where Id=#{Id}") int updateUser(user user);如果只有一个基本类型,可以忽略,但是建议加上
在SQL中引用的就是@Param()中设置的属性名字
idea构造方法的插件,一般不用
测试环境搭建
新建实体类Teacher、Student
建立Mapper接口
建立Mapper.xml文件
在核心配置文件中绑定注册我们的Mapper接口或者文件
<mappers> <!--绑定class--> <mapper class="com.bjpowernode.dao.TeacherMapper"/> <mapper class="com.bjpowernode.dao.StudentMapper"/> <!--第二种:绑定mapper配置文件--> </mappers>测试
案例:需求:查找所有学生,即对应的老师
方式1:按照查询嵌套处理:对应于子查询 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.bjpowernode.dao.StudentMapper"> <!--查询所有学生的信息,以及对应的老师的信息 1. 查询所有的学生 2. 根据查询出来的学生的id,寻找对应的老师 --> <select id="SelectStudent" resultMap="StudentTeacher"> select * from student </select> <!--StudentTeacher表示查询出来的学生对应的老师 ,进行结果集映射--> <resultMap id="StudentTeacher" type="Student"> <result property="id" column="id" /> <result property="name" column="name"/> <!--复杂属性:需要单独处理 对象:association 集合:collection--> <association property="teacher" column="tid" javaType="Teacher" select="selectTeacher"/> </resultMap> <select id="selectTeacher" resultType="Teacher"> select * from teacher where id=#{id}; </select> </mapper>方式2:按照结果映射处理:对应于连表查询
<!--第二种:按照结果映射查询--> <!--查询语句 select s.id sid, s.name sname, t.name from student s, teacher t where sid = tid --> <select id="SelectStudent2" resultMap="StudentTeacher2"> select s.id sid, s.name sname, t.id tid, t.name tname from student s, teacher t where s.tid = t.id </select> <!--type指java实体类的类型,这儿查询出来的是一个Student类--> <resultMap id="StudentTeacher2" type="Student"> <!--property是java实体类的类型的属性名字 column是数据库的名字--> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> </association> </resultMap>按照查询嵌套处理:对应于子查询
<!--方式1:按照查询映射处理,对应于子查询--> <select id="selectTeacher2" resultMap="TeacherStudent2"> select * from teacher </select> <resultMap id="TeacherStudent2" type="Teacher"> <result property="id" column="id"/> <result property="name" column="name"/> <collection property="student" column="id" select="studentselect" javaType="ArrayList" ofType="Student"/> </resultMap> <select id="studentselect" resultType="Student"> select * from student where tid=#{id} </select>注意点:
保证SQL的可读性,尽量保证通俗易懂注意一对多和多对一中,属性名字和字段的问题如果问题不好排查错误,可以使用日志,建议使用log4j面试高频(慢sql):
Mysql引擎InnoDB底层原理索引索引优化动态SQL:根据不同的条件生成不同的SQL语句
动态SQL就是拼接sql语句,首先需要保证sql语句正确
所以在写的时候,建议首先写出正确的sql语句,然后再在mybatis中写出对应的操作语句
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<select id="selectBlogByIfWhere" parameterType="map" resultType="blog"> select * from blog <where> <!--智能的自动拼接sql--> <if test="title != null"> title=#{title} </if> <if test="author != null"> and author=#{author} </if> </where> </select> 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
下面的语句实现选择查询:如果传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG
<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标签抽取公共部分,实现代码复用,在使用地方使用include标签引用即可
注意事项:
1. 最好基于单表来定义sql片段; 2. 不要存在where标签 <sql id="sqlpianduan"> <if test="title != null"> and title=#{title} </if> <if test="author != null"> and author=#{author} </if> </sql> <!--动态Sql之if的用法--> <select id="selectBlogByIf" parameterType="map" resultType="blog"> select * from blog where 1=1 <include refid="sqlpianduan"></include> </select>动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。
<!--参数解释:select * from blog where (id = 1 or id = 2 or id = 3) collection 传入的进行遍历的list集合 item 集合中的每一个元素 open 拼接的sql以什么开头, 这里以"("开头 close 拼接的sql以什么结尾,这里以")"开头 separator 以什么分割,这里是or index 起始下标 这里没有(默认从头开始) --> <select id="selectBlogByForeach" parameterType="map" resultType="blog"> select * from blog <where> <foreach collection="ids" item="id" separator=" or " open=" (" close=" )"> id=#{id} </foreach> </where> </select> //测试foreach的测试函数 @Test public void selectBlogByForeach(){ SqlSession sqlSession = null; try { sqlSession = utils.getsqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); //传入一个map集合,集合中的元素就是需要遍历的list集合 HashMap map = new HashMap(); List<Integer> ids = new ArrayList<>(); ids.add(1); ids.add(2); ids.add(3); //注意这儿的key需要与xml文件中的foreach语句中名字对应,否则找不到 //比如map中放的key应该和collection="ids"名字一致 map.put("ids",ids); List<Blog> blogs = mapper.selectBlogByForeach(map); for (Blog blog : blogs) { System.out.println(blog); } }finally { sqlSession.close(); } }定义:存在内存中的临时数据叫做缓存
目的:提高查询速度
能够使用缓存的数据:经常使用且不经常改变的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b97vy4Un-1601702455047)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201002105704407.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9bTYiR9c-1601702455049)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201002110137484.png)]
二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存基于namespace级别的缓存,一个名称空间,对应一个二级缓存工作机制 一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是:会话关闭了,一级缓存中的数据会被保存到二级缓存中新的会话查询信息,就可以从二级缓存中获取内容不同的mapper查出的数据会放在自己对应的缓存中步骤:
开启全局缓存
<!--配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>在要使用二级缓存的mapper中开启
<cache/>查询时,先看二级缓存有没有数据,再看一级缓存有没有,否则走数据库。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRiu7hDU-1601702455051)(C:\Users\李柏松\AppData\Roaming\Typora\typora-user-images\image-20201002130721204.png)]
。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存基于namespace级别的缓存,一个名称空间,对应一个二级缓存工作机制 一个会话查询一条数据,这个数据会被放在当前会话的一级缓存中如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是:会话关闭了,一级缓存中的数据会被保存到二级缓存中新的会话查询信息,就可以从二级缓存中获取内容不同的mapper查出的数据会放在自己对应的缓存中步骤:
开启全局缓存
<!--配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>在要使用二级缓存的mapper中开启
<cache/>查询时,先看二级缓存有没有数据,再看一级缓存有没有,否则走数据库。
[外链图片转存中…(img-nRiu7hDU-1601702455051)]
Redis数据库来做缓存