Mybatis 1、简介 1.1 什么是Mybatis
MyBatis 是一款优秀的持久层框架
它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1 2 3 4 5 <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.2</version > </dependency >
1.2 持久层 数据持久化
将程序的数据在持久态和瞬时状态转换。(数据库,io)
持久层:
2、Mybatis程序示例 步骤:环境搭建—–>导入依赖—->编码
2.1 搭建环境导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.21</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > </dependencies >
2.2 创建子项目/模块
编写Mybatis核心配置文件
mybatis-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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 ="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?useSSL=true& useUnicode=true& characterEncoding=utf-8& serverTimezone=Asia/Shanghai" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/pans/dao/UserMapper.xml" /> //注意添加dao映射关系 </mappers > </configuration >
编写Mybatis工具类(连接数据库)
MybatisUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.pans.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; public static SqlSession getSqlSession () { try { String resource = "mybatis-config.xml" ; InputStream inputStream = null ; inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream); } catch (IOException e) { e.printStackTrace(); } return sqlSessionFactory.openSession(); } }
Dao接口
1 2 3 4 5 6 7 8 9 10 package com.pans.dao;import com.pans.pojo.User;import java.util.List;public interface UserDao { public List<User> getUserList () ; }
==接口实现==
由原来的实现类变成xml文件UserMapper.xml
1 2 3 4 5 6 7 8 9 <?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.pans.dao.UserDao" > <select id ="getUserList" resultType ="com.pans.pojo.User" > select * from mybatis.user </select > </mapper >
==注意:==
namespace: 表示需要实现的接口类
id: 需要实现的接口类里面的方法
**resultType:**方法的返回类型
parameterType :方法的参数类型
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.pans.dao;import com.pans.pojo.User;import com.pans.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;public class UserDaoTest { @Test public void test () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userList = mapper.getUserList(); for (User suser : userList) { System.out.println(suser); } sqlSession.close(); } }
文件结构:
常见问题:
Maven导出资源问题java.io.IOException: Could not find resource mybatis-config.xml
解决办法:pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build >
配置文件未注册
接口绑定错误
方法名错误
返回类型错误
3.3 CRUD 需求1:根据用户id查询用户信息
在Dao(Mapper层)添加相应接口方法
1 2 public User getUserById (int id) ;
在xml文件中实现查询语句并绑定接口方法(一个方法对应一个sql标签语句)
1 2 3 <select id ="getUserById" parameterType ="int" resultType ="com.pans.pojo.User" > select * from mybatis.user where id=1; </select >
测试
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testGetUserById () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); User user = mapper.getUserById(1 ); System.out.println(user); sqlSession.close(); }
需求2:增加/修改/删除 用户
Dao接口:
1 2 3 4 5 6 7 8 public void addUser (User user) ;public void upDateUser (User user) ;public void deleteUser (int id) ;
接口对应xml
1 2 3 4 5 6 7 8 9 10 11 12 <insert id ="addUser" parameterType ="com.pans.pojo.User" > insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd}); </insert > <update id ="upDateUser" parameterType ="com.pans.pojo.User" > update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}; </update > <delete id ="deleteUser" parameterType ="int" > delete from mybatis.user where id=#{id}; </delete >
==注意: ==
当接口参数个数大于1时该如何处理?
注意在增删改时最后必须提交事务
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Test public void testAddUser () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.addUser(new User (5 ,"apa" ,"3412" )); sqlSession.commit(); sqlSession.close(); } @Test public void testUpdateUser () { SqlSession sqlSession=MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.upDateUser(new User (4 ,"hello" ,"world" )); sqlSession.commit(); sqlSession.close(); } @Test public void testDeleteUser () { SqlSession sqlSession=MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.deleteUser(5 ); sqlSession.commit(); sqlSession.commit(); }
3.4 Map(多参数) Dao接口:
1 2 public void addUser2 (Map map) ;
xml:
1 2 3 4 <insert id ="addUser2" parameterType ="map" > insert into user (id,name,pwd) values(#{userId},#{userName},#{userPwd}); </insert >
Test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void testAddUser2 () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); HashMap<Object, Object> map = new HashMap <>(); map.put("userId" ,6 ); map.put("userName" ,"asdasd" ); map.put("userPwd" ,"dasdas" ); mapper.addUser2(map); sqlSession.commit(); sqlSession.commit(); }
3.5 模糊查询 Dao接口:
1 2 public List<User> getUserLike (String subName) ;
mapper.xml——>1
1 2 3 <select id ="getUserLike" parameterType ="String" resultType ="com.pans.pojo.User" > select * from user where name like %#{value}% </select >
mapper.xml——->2
1 2 3 <select id ="getUserLike" parameterType ="String" resultType ="com.pans.pojo.User" > select * from user where name like #{value} </select >
Test—–>1
1 2 3 4 5 6 7 8 9 10 11 @Test public void testGetUserLike () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> user = mapper.getUserLike("张" ); for (User user1 : user) { System.out.println(user1); } sqlSession.close(); }
Test—>2
1 2 3 4 5 6 7 8 9 10 11 @Test public void testGetUserLike () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> user = mapper.getUserLike("张%" ); for (User user1 : user) { System.out.println(user1); } sqlSession.close(); }
思考:
方式1是在sql里面写通配符
方式2是在查询语句中写通配符
此时两种方式是否存在sql注入问题!
3、配置解析 3.1 属性优化 mybatis-config.xml里面的properties属性可以引入外部配置文件db.properties
1 2 3 4 driver =com.mysql.cj.jdbc.Driver url =jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username =root password =123456
mybatis-config.xml中的标签必须按照规定顺序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <configuration > <properties resource ="db.properties" > <property name ="username" value ="root" /> </properties > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="${driver}" /> <property name ="url" value ="${url}" /> <property name ="username" value ="${username}" /> <property name ="password" value ="${password}" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/pans/dao/UserMapper.xml" /> </mappers > </configuration >
3.3 别名优化(不区分大小写) ==Java编译后的字节码文件名不区分大小写,即同一个包下不能有两个仅有大小写区分的类名==
在mybatis-config.xml可以使用typeAliases便签进行实体类别名优化: 它仅用于 XML 配置,意在降低冗余的全限定类名书写
1 2 3 <typeAliases> <typeAlias type="com.pans.pojo.User" alias="User"/> </typeAliases>
对应的mapper.xml
1 2 3 <select id ="getUserList" resultType ="User" > select * from mybatis.user </select >
也可以指定一个包名,实体类的每一个类的别名为其首字母小写!(大写也可以)
1 2 3 <typeAliases> <typeAlias type="com.pans.pojo"/> </typeAliases>
也可在实体类中加入注解Alias作为其别名
1 2 3 4 5 6 7 @Alias("user") public class User { public User (long id, String name, String pwd) { this .id = id; this .name = name; this .pwd = pwd; }
3.4 设置
数据库不区分大小写,故用下划线区分!
3.5 映射器(Mapper) 方式一:
1 2 3 <mappers > <mapper resource ="com/pans/dao/UserMapper.xml" /> </mappers >
方式二:使用class文件注册
1 2 3 4 <mappers > <mapper class ="com.pans.dao.UserMapper" /> </mappers >
注意:接口和其配置文件必须同名且在同一个包下!!!
方式三:使用包名
1 2 3 <mappers > <package name ="com.pans.dao" /> </mappers >
3.6 生命周期和作用域 用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题 。
SqlSessionFactoryBuilder:
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要(用于局部方法变量);
SqlSessionFactory: (数据库连接池)
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例(用于应用作用域);
可以使用单例模式或静态单例模式。
SqlSession: (数据库请求,用完需要关闭)
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的(请求或方法作用域)
4、属性名和字段名不一致问题 数据库中的字段和实体类中的字段名不一致的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 <resultMap id ="UserMap" type ="User" > <result column ="id" property ="id" /> <result column ="name" property ="name" /> <result column ="pwd" property ="password" /> </resultMap > <select id ="getUserList" resultMap ="UserMap" > select id,name,pwd from mybatis.user where id=${id} </select >
5、日志
标准日志工厂
1 2 3 <settings > <setting name ="logImpl" value ="STDOUT_LOGGING" /> </settings >
Log4j
1 2 3 4 5 6 7 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 log4j.rootLogger = stdout,D log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File =./log/pans-info.log log4j.appender.D.Append = true log4j.appender.D.MaxFileSize = 10mb log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
1 2 3 4 5 6 7 8 9 @Test public void testLog4j () { Logger logger = Logger.getLogger(UserDaoTest.class); logger.info("进入Log4j=====>info方法!" ); logger.debug("进入Log4j=====>debug方法!" ); logger.error("进入Log4j=====>error方法!" ); }
6、分页
1 select * from user limit startIndex,pageSize;
除了修改sql语句,还需使用map
1 2 3 4 <select id ="getUserByPage" parameterType ="map" resultMap ="userList" > select * from user limit #{startIndex},#{pageSize}; </select >
1 2 3 Map<String, Integer> map = new HashMap <>(); map.put("startIndex" ,2 ); map.put("pageSize" ,2 );
7、使用注解开发 直接在接口上方使用注解写sql语句,并在xml配置文件mapper属性改为class接口。(自动提交事务)
1 2 @Select("select * from user") public User getUserList () ;
1 2 3 @Select("select * from user where id=${id}") public User getUserById (@Param("id") int id) ;
关于@param :
基本类型为参数或者String类型,需要加上
引用类型可以不用加
8、Lombok 直接在实体类前加@Data可以自动生成get/set/constructer等方法。。。
9、多对一处理
子查询
联表查询
1 2 3 SELECT u.`id`,u.`name`,t.`name` FROM `user ` AS u,`teacher`AS tWHERE u.`teacher_id`= t.`id`;
StudentMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <mapper namespace ="com.pans.dao.StudentMapper" > <select id ="getStudentList" resultMap ="studentList" > SELECT s.`id` id,s.`name` sname,t.`name` tname FROM teacher t,student s WHERE s.tid=t.id; </select > <resultMap id ="studentList" type ="student" > <result property ="id" column ="id" /> <result property ="name" column ="sname" /> <association property ="teacher" javaType ="Teacher" > <result property ="name" column ="tname" /> </association > </resultMap > </mapper >
注意: 实体类属性与数据库列并不是一一对应:(即属性名与字段不一致)
1 2 3 4 5 6 7 8 public class Student { private long id; private String name; private Teacher teacher; }
10、一对多处理(略) 11、动态SQL 何为动态sql:
==动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式进行组合,而无需在业务层面就行查询。==
导包
1 2 3 4 5 6 7 8 <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.20</version > <scope > provided</scope > </dependency >
编写配置文件(注意开启驼峰命名自动转换)
1 2 3 4 5 6 <mappers > <mapper resource ="com/pans/dao/BlogMapper.xml" /> </mappers > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
编写实体类及其Mapper接口及其实现的xml文件
1 2 3 4 5 6 7 8 9 10 @AllArgsConstructor @NoArgsConstructor public class Blog { private String id; private String title; private String author; private Date createTime; private long views;
1 2 3 4 public interface BlogMapper { public void addBlog (Blog blog) ; }
1 2 3 4 5 6 7 8 <mapper namespace ="com.pans.dao.BlogMapper" > <insert id ="addBlog" parameterType ="blog" > insert into blog(id,title,author,create_time,views) values(#{id},#{title},#{author},#{createTime},#{views}); </insert > </mapper >
编写测试文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import com.pans.dao.BlogMapper;import com.pans.pojo.Blog;import com.pans.utils.IDutils;import com.pans.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.Date;public class Mytest { @Test public void addBlogTest () { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog (); blog.setId(IDutils.getID()); blog.setTitle("Mybatis" ); blog.setAuthor("狂神说" ); blog.setCreateTime(new Date ()); blog.setViews(9999 ); mapper.addBlog(blog); blog.setId(IDutils.getID()); blog.setTitle("Java" ); mapper.addBlog(blog); blog.setId(IDutils.getID()); blog.setTitle("Spring" ); mapper.addBlog(blog); blog.setId(IDutils.getID()); blog.setTitle("微服务" ); mapper.addBlog(blog); sqlSession.commit(); sqlSession.close(); } }
1、 if语句
mapper接口
1 2 public List<Blog> queryByIf (Map map) ;
mapper配置文件
1 2 3 4 5 6 7 8 9 10 11 <select id ="queryByIf" parameterType ="map" resultType ="Blog" > select * from blog where 1=1 <if test ="title != null" > and title=#{title}; </if > <if test ="author!=null" > and author="狂神说"; </if > </select >
测试文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testQueryByIf () { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); Map map = new HashMap (); map.put("author" ,"dasd" ); List<Blog> blogs = mapper.queryByIf(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
2、where语句 类似Java中的switch语句,并且where便签能自动识别第一个成立条件并去掉可能存在的and/or。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="queryByWhere" resultType ="blog" parameterType ="map" > select * from blog <where > <choose > <when test ="titile!=null" > title=#{title} </when > <when test ="author!=null" > author=#{author} </when > <otherwise > views=#{views} </otherwise > </choose > </where > </select >
3、set语句 set元素会动态前置SET关键字,同时也会删掉无关的逗号
4、foreach 集合遍历
12、缓存 1、一级缓存 一级缓存(本地会话缓存)默认开启,只在一次sqlSession中有效!
查询相同数据的时候直接从缓存中获取,而无需经过数据库查询,提高系统效率。
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
会使用(LRU,FIFO)等算法来刷新缓存。
2、二级缓存 开启方式:加标签<cache/>
,又称全局缓存,可跨sqlSession缓存!
3、自定义缓存