Mybatis完整版详解
程序那点事2022-08-12 18:57湖南
一、简介#
1.什么是MyBatis#
-
MyBatis 是一款优秀的持久层框架
-
它支持自定义 SQL、存储过程以及高级映射。
-
MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
-
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
-
MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。
-
2013年11月迁移到Github。
(1)如何获得MyBatis#
-
maven仓库
Copy<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version></dependency>
-
GitHub:https://github.com/mybatis/mybatis-3/releases
-
中文文档:https://mybatis.org/mybatis-3/index.html
2.持久化#
数据持久化
-
持久化就是将程序的数据在持久状态和瞬时状态转化的过程
-
内存:断电即失
-
数据库(jdbc),io文件持久化
为什么需要持久化
-
有一些对象,不能让它丢掉
-
内存太贵了
3.持久层#
Dao层,Service层,Controller层 什么叫层
-
完成持久化工作的代码块
-
层是界限十分明显的
4.为什么需要Mybatis#
-
帮助程序员将数据存入到数据库中
-
方便
-
传统的JDBC代码太复杂了,Mybatis对其进行了简化,
二、第一个Mybatis程序#
思路:搭建环境-->导入Mybatis-->编写代码-->测试
1.搭建环境#
搭建一个数据库
新建一个maven项目,并导入maven依赖,要注意导入mybatis时需要手动开启Tomcat的bin目录下startup.sh(只针对本机,Windows开启startup.bat)
Copy <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> </dependency> </dependencies>
2.创建一个模块#
编写mybatis的核心配置文件#
mybatis-config.xml文件代码
Copy <?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"><!-- &在xml文件中与符号需要这样来转义--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="root123456"/> </dataSource> </environment> </environments><!-- 每一个mapper.xml都需要在Mybatis核心配置文件中注册--> <mappers> <mapper resource="com/tang/dao/UserMapper.xml"/> </mappers></configuration>
注意:这里如果没写加载驱动的话会报以下错误
org.apache.ibatis.exceptions.PersistenceException: Error querying database. Cause: java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "key" is null
但是写了又会说会自动加载,加载多余,不过这并不是错误,因此还是写上安全
编写mybatis工具类#
Copy//sqlSessionFactory-->sqlSessionpublic class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static{ try { //使用Mybatis第一步,获取sqlSessionFactory对象 //这三行代码是从mybatis中文文档中获取到的,规定这么写的 String resource = "mybatis-config.xml";//这里写上自己的mybatis配置文件的文件名即可 InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。 public static SqlSession getSqlSession(){ SqlSession sqlSession = sqlSessionFactory.openSession(); return sqlSession; }}
3.编写代码#
实体类#
字段名和数据里的字段一一对应
Copypublic class User { private int id; private String name; private String pwd; public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; }}
Dao接口#
Copypublic interface UserDao { List<User> getUserList();}
接口实现类由Impl转为一个Mapper配置文件#
Copy<?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"><!--namespace=绑定一个对应的Dao/Mapper接口,等价于以前去实现接口并重写方法--><mapper namespace="com.tang.dao.UserDao"> <!-- select查询语句 --> <!--id等价于以前去实现接口并重写方法 resultType:执行sql返回的结果集,仅需要返回接口的方法中的泛型类型即可 --> <select id="getUserList" resultType="com.tang.pojo.User"> select * from mybatis.user </select></mapper>
4.测试#
注意点:
-
若在接口的配置文件中没有写以下代码则会报下面的错
Copy<mappers> <mapper resource="com/tang/dao/UserMapper.xml"/> </mappers>
org.apache.ibatis.binding.BindingException: Type interface com.tang.dao.UserDao is not known to the MapperRegistry.
-
若在pom中没有以下代码则resources下的配置文件和java目录下的xml配置文件就不会被打包,也就是在target中并没有相应的class文件
Copy<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>
*测试代码:
Copy public class UserDaoTest { @Test public void test(){ //第一步:获得sqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //方式一:getMapper 执行SQL UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for(User user: userList){ System.out.println(user); } //关闭SQLSession sqlSession.close(); }}
运行结果图
三、CRUD(增删改查)#
1.Select#
选择,查询语句
-
id:就是对应namespace中的方法名
-
resultType:Sql语句执行的返回值
-
parameterType:参数类型
编写接口#
Copy//查询指定id的用户 User getUserById(int id);
编写对应Dao中的sql语句#
Copy<select id="getUserById" parameterType="int" resultType="com.tang.pojo.User"> select * from mybatis.user where id= #{id} </select>
测试#
Copy//查询指定用户 @Test public void getUserByID(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }
2.Insert#
编写接口#
Copy//添加一个用户 int addUser(User user);
编写对应Dao中的sql语句#
Copy <insert id="addUser" parameterType="com.tang.pojo.User"> insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd}) </insert>
测试#
Copy//添加用户 @Test public void addUserTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.addUser(new User(4,"twq","1233")); sqlSession.commit();//增删改必须要提交事务,否则在数据库中就无法查看增删改后的结果 sqlSession.close(); }
3.update#
编写接口#
Copy//修改一个用户 int updateUser(User user);
编写对应Dao中的sql语句#
Copy <update id="updateUser" parameterType="com.tang.pojo.User"> update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}; </update>
测试#
Copy//修改用户 @Test public void updateUserTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.updateUser(new User(1,"唐","1234")); sqlSession.commit(); sqlSession.close(); }
4.delete#
编写接口#
Copy//删除一个用户 int deleteUser(int id);
编写对应Dao中的sql语句#
Copy<delete id="deleteUser" parameterType="int"> delete from mybatis.user where id=#{id}; </delete>
测试#
Copy@Test public void deleteUserTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.deleteUser(2); sqlSession.commit(); sqlSession.close(); }
运行前user中表的数据
运行增删改查之后结果图
5.万能的map#
目的:将user表中id=1的name改为“唐三唐昊” 接口代码
Copy//万能的map int updateUser2(Map<String,Object> map);
对应Dao中sql代码
Copy <update id="updateUser2" parameterType="map">-- 这里就没有必要在把user中的所有字段都写进来,用到哪个就可以写哪个字段,且传进去的字段名可以任意写 update mybatis.user set name=#{username} where id=#{userid}; </update>
测试代码
Copy //map实现用户修改 @Test public void updateUser2Test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("userid",1); map.put("username","唐三唐昊"); mapper.updateUser2(map); sqlSession.commit();//增删改必须要提交事务,否则在数据库中就无法查看增删改后的结果 sqlSession.close(); }
运行结果图
Map传递参数,直接在sql中取出key即可
对象传递参数,直接在sql中取对象的属性即可
只有一个基本类型参数的情况下,可以直接在sql中取到
6.模糊查询#
目的:利用模糊查询 查询所有姓唐的人 接口
Copy List<User> getUserLike(String value);
sql代码
Copy <select id="getUserLike" resultType="com.tang.pojo.User"> select *from mybatis.user where name like #{value} </select>
测试代码
在Java代码执行的时候,传递通配符% %,不会存在sql注入的问题
Copy //模糊查询 @Test public void getUserLikeTest(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userLike = mapper.getUserLike("%唐%"); for(User user: userLike){ System.out.println(user); } sqlSession.close(); }
在sql拼接中使用通配符,存在sql注入问题
Copy<select id="getUserLike" resultType="com.tang.pojo.User"> select *from mybatis.user where name like "%"#{value}"%" </select>
CopyList<User> userLike = mapper.getUserLike("唐");
两种情况的运行结果
四、配置解析#
1.核心配置文件#
-
mybatis-config.xml
-
Mybatis的配置文件包含了会深深影响Mybatis行为的设置和属性信息
-
configuration(配置)
-
environment(环境变量)
-
transactionManager(事务管理器)
-
dataSource(数据源)
-
properties(属性)
-
settings(设置)
-
typeAliases(类型别名)
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂)
-
plugins(插件)
-
environments(环境配置)
-
databaseIdProvider(数据库厂商标识)
-
mappers(映射器)
-
2.环境配置(environments)#
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
学会使用配置多套运行环境,比如如下这种方式就可以选择id为test的配置环境,虽然有多套配置环境,但是最终运行的只会是其中一种
Copy<configuration> <environments default="test"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"><!-- &在xml文件中与符号需要这样来转义--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="root123456"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- &在xml文件中与符号需要这样来转义--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="root123456"/> </dataSource> </environment> </environments><!-- 每一个mapper.xml都需要在Mybatis核心配置文件中注册--> <mappers> <mapper resource="com/tang/dao/UserMapper.xml"/> </mappers></configuration>
Mybatis默认的事务管理器就是JDBC,连接池为POOLED
3.属性(properties)#
我们可以通过properties属性来实现引用配置文件
这些属性都是可外部配置且可动态替换的,既可以在典型的Java属性文件中配置,也可通过properties元素的子元素来传递【db.properties】
编写一个配置文件
db.properties
Copydriver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"username=rootpassword=root123456
在核心配置文件中引入
Copy<!--引入外部配置文件--> <properties resource="db.properties"/>
然后就可以通过如下的方式去读取db.properties文件里的值
Copy<property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/>
-
可以直接引入外部文件
-
可以在其中增加一些属性配置
-
如果两个文件有同一个字段,优先使用外部配置文件中的
4.类型别名(typeAliases)#
作用
-
类型别名可为 Java 类型设置一个缩写名字。
-
它仅用于 XML 配置,意在降低冗余的全限定类名书写
Copy<!--可以给实体类起别名--> <typeAliases> <typeAlias type="com.tang.pojo.User" alias="User"></typeAlias> </typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
扫描实体类的包,它的默认别名就为这个类的 类名,首字母小写,大写也行!
如下代码在调用到pojo包下面的类的时候可以直接使用类名的小写字母完成
Copy<typeAliases> <package name="com.tang.pojo"/></typeAliases>
在实体类比较少的时候使用第一种方式
如果实体类比较多,建议使用第二种
第一种可以DIY起别名,第二种则不行,如果非要改,需要在实体类上增加注解
在实体类上加注解给类名起别名
Copy@Alias("user")public class User {
5.设置(settings)#
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为
6.映射器(mappers)#
MapperRegistry:注册绑定我们的Mapper文件;
方式一:【推荐使用】
Copy<!-- 每一个mapper.xml都需要在Mybatis核心配置文件中注册--> <mappers> <mapper resource="com/tang/dao/UserMapper.xml"/> </mappers>
方式二:使用class文件绑定注册
Copy<mappers> <mapper class="com.tang.dao.UserMapper"/></mappers>
注意点
-
接口和它的Mapper配置文件必须同名
-
接口和它的Mapper配置文件必须在同一个包下