mybatis
01. 原始JDBC操作的分析
>> 问题
> 数据库连接的创建、释放频繁,造成系统资源浪费从而影响系统性能;
> sql语句在代码中硬编码,造成代码不易维护,实际应用中的sql变化可能较大,sql变动需要改变java代码;
> 查询操作时,需要手动将结果集中的数据封装到实体中,插入操作时,需要手动将实体的数据设置到sql语句的占位符的位置;
>> 应对方案
> 使用数据库连接池初始化连接资源;
> 将sql语句抽取到xml配置文件中;
> 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射;
02. mybatis 配置文件头信息
** 在使用 mybatis 时,有二种文件,一种是基于模块的应用功能进行配置的文件,即:mapper 文件; 一般命名为: XxxxMapper.xml
另外一种,是对整个 mybatis 进行配置的配置文件,即主配置文件; 一般命名为 sqlMapConfig.xml
在实际使用时,命名没有特殊要求,自行命名即可;
>> 基于模块应用的配置文件配置信息
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3
4 <mapper namespace="sysRole"> //命名空间定义
5 <select id="findAll" resultType="club.wuyu.domain.SysRole"> //sql对应id及返回值对应封装类型
6 select * from sys_Role //要执行的sql
7 </select>
8 </mapper>
>> mybatis 主配置文件
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
3
4 <configuration>
5 <environments default="dev">
6 <environment id="dev">
7 <transactionManager type="JDBC"></transactionManager>
8 <dataSource type="POOLED">
9 <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
10 <property name="url" value="jdbc:mysql://localhost:3306/test"/>
11 <property name="username" value="root"/>
12 <property name="password" value="123456"/>
13 </dataSource>
14 </environment>
15 </environments>
16
17 <mappers> //业务mapper配置文件的登记
18 <mapper resource="club/wuyu/mapper/SysRoleMapper.xml"/>
19 </mappers>
20 </configuration>
>> 为确保在发布项目时,mapper 相关配置文件与相应的domain, dao, service 发布到同一位置,在做配置文件时,同样需要配置相应的包路径
* Resources 上右键 > new Directory > 输入: club/wuyu/mapper 才能生成三级目录;
若直接输入 club.wuyu.mapper 将生成一级目录,文件夹的名称为 club.wuyu.mapper (这一点与在java中建包时不一样)
>> 测试代码
1 @Test
2 public void test1() throws IOException {
3 //加载核心配置文件
4 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
5 //获取sqlSession工厂对象
6 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
7 //通过工厂对象,获得sqlSession对象
8 SqlSession sqlSession = sessionFactory.openSession();
9 //执行sql语句
10 List<SysRole> sysRoleList = sqlSession.selectList("sysRole.findAll");
11 //展示结果
12 System.out.println(sysRoleList);
13 //释放资源
14 sqlSession.close();
15 }
03. 插入操作 (修改操作与插入操作一样,配置文件中使用 update 而已,关于参数的类型及参数值的使用,与insert 一样)
1 <!--插入操作--> 2 <insert id="save" parameterType="club.wuyu.domain.SysRole"> //使用 parameterType 标识参数类型 3 insert into user values (#{id},#{roleName},#{roleDesc}) //使用 #{实体属性名} 接收参数值 4 </insert>
1 @Test 2 public void test2() throws IOException { 3 //加载核心配置文件 4 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); 5 //获取sqlSession工厂对象 6 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 7 //通过工厂对象,获得sqlSession对象 8 SqlSession sqlSession = sessionFactory.openSession(); 9 //执行操作 10 SysRole sysRole = new SysRole(); 11 sysRole.setRoleName("测试"); 12 sysRole.setRoleDesc("测试用的一个分组"); 13 14 sqlSession.insert("sysRole.save",sysRole); 15 sqlSession.commit(); //执行插入操作后,需要提交事务,否则数据不会进数据库 16 17 //释放资源 18 sqlSession.close(); 19 }
04.关于简单数据类型参数的处理
1 <!--删除操作--> 2 <delete id="delete" parameterType="java.lang.Integer"> //简单数据类型参数的定义 3 delete from sys_Role where id=#{id} //参数值的引用,任意 ; #{id} ,也可以使用 #{a},#{b}... 4 </delete>
1 //执行操作 2 sqlSession.delete("sysRole.delete",8); 3 sqlSession.commit();
** 在配置mapper文件及执行操作时,因手误,在配置文件中,使用了 <update>标签,sql还是 delete
在执行时,使用了 insert
最终结果:依然是删除了数据;
>> 各标签与函数的使用,之所以有insert , update ,delete ,主要还是在于语义的理解上,毕竟真实执行的sql还是delete;
05. 核心配置文件的层级结构
>> 关于 environment 环境变量参数的解析
> transactionmanager: 事务管理器;
* JDBC: 直接使用 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域;
* MANAGED: 这个配置几乎没做什么;从来不提交或回滚一个连接;而是让容器来管理事务的整个生命周期(如:JEE应用服务器的上下文);
默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为;
> dataSource : 数据源
* UNPOOLED: 这个数据源的实现只是每次被请求时打开和关闭连接;
* POOLED:这种数据源的实现,利用"池"的概念将JDBC连接对象组织起来;
* JNDI: 这个数据源的实现,是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用;
05. 关于 jdbc.properties 数据库连接配置参数抽取处理
>> 抽取数据库连接参数到配置文件 jdbc.properties
1 jdbc.driver=com.mysql.cj.jdbc.Driver 2 jdbc.url=jdbc:mysql://localhost:3306/test 3 jdbc.username=root 4 jdbc.password=123456
>> mybatis 主配置文件中对 jdbc.properties 配置文件的引用 (注意:是在 configuration 标签体内)
1 <configuration> 2 <properties resource="jdbc.properties"/> 3 4 <environments default="dev"> 5 <environment id="dev"> 6 <transactionManager type="JDBC"></transactionManager> 7 <dataSource type="POOLED"> 8 <property name="driver" value="${jdbc.driver}"/> 9 <property name="url" value="${jdbc.url}"/> 10 <property name="username" value="${jdbc.username}"/> 11 <property name="password" value="${jdbc.password}"/> 12 </dataSource> 13 </environment> 14 </environments> 15 16 <mappers> 17 <mapper resource="club/wuyu/mapper/SysRoleMapper.xml"/> 18 </mappers> 19 </configuration>
06. 关于 typeAliases 别名的配置 ( configuration 标签体内)
按顺序说明,将 typeAliases 标签体整体,放到 <properties > 后面就可以了
07. mybatis 在Dao层的常规实现
>> 引入 mybatis 后,Dao层的命名规则在某些企业会调整为: mapper;
如: 之前的包名叫 dao, 内部接口为 XxxxDao, 实现类叫: XxxxDaoImpl
调整后包包叫 mapper, 内部接口为 XxxxMapper, 实现类叫: XxxxMapperImpl
>> 整体的处理,无外乎是在测试中的整套内容,转移到 mapper的实现类中
1 public class SysRoleMapperImpl implements SysRoleMapper { 2 @Override 3 public List<SysRole> fincAll() throws IOException { 4 //资源加载 5 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); 6 //工厂创建 7 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 //打开上下文 9 SqlSession sqlSession = sessionFactory.openSession(true); 10 //执行 11 List<SysRole> sysRoleList = sqlSession.selectList("sysRole.findAll"); 12 13 return sysRoleList; 14 } 15 }
08. Mybatis 在 Dao层的代理开发实现
>> 采用 Mybatis 的代理开发方式实现Dao层的开发,这种方式是企业中的常用方式;
>> Mapper 接口开发方法,只需要编写 Mapper 的接口 (相当于dao接口), 由 mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边常规开发模式中的 SysRoleMapperImpl 接口实现类一样;
>> Mapper接口开发需要遵循如下的规范
a. 对应的模块应用配置文件中,命名空间 namespace 与 mapper 接口的全限定名相同;
b. 接口方法名和应用配置文件中每个statement 的 id 相同;
c. 接口方法的输入参数类型和配置文件中定义的每个sql的 parameterType 的类型相同;
d. 接口方法的输出参数类型和配置文件中定义的每个sql的 resultType 的类型相同;
>> 实现
> 此时只需要配置 接口 的定义即可;
> 在service层,
1 @Override 2 public List<SysRole> fincAll() throws IOException { 3 //资源加载 4 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); 5 //工厂创建 6 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 7 //打开上下文 8 SqlSession sqlSession = sessionFactory.openSession(true); 9 10 //代理方式(此方式,只需按规范定义接口及配置文件,需要实现) 11 SysRoleMapper mapper = sqlSession.getMapper(SysRoleMapper.class); 12 List<SysRole> sysRoleList = mapper.fincAll(); 13 //常规实现方式(此方式,需要对接口做实现) 14 //List<SysRole> sysRoleList = sqlSession.selectList("sysRole.findAll"); 15 16 return sysRoleList; 17 }
09. mybatis 中的动态sql配置
>> if 条件判断
1 <select id="findByCondition" resultType="club.wuyu.domain.SysRole" parameterType="club.wuyu.domain.SysRole"> 2 select * from sys_Role 3 <where> 4 <if test="id!=null"> 5 and id=#{id} 6 </if> 7 <if test="roleName!=null"> 8 and roleName=#{roleName} 9 </if> 10 <if test="roleDesc!=null"> 11 and roleDesc=#{roleDesc} 12 </if> 13 </where> 14 </select>
>> foreach 组合
1 <select id="findByIDS" resultType="club.wuyu.domain.SysRole" parameterType="list"> 2 select * from sys_Role 3 <where> 4 <foreach collection="list" open=" id in (" close=")" item="id" separator=","> 5 #{id} 6 </foreach> 7 </where> 8 </select>
>> sql片断的抽取
1 <!--查询操作--> 2 <sql id="selectSysRole">select * from sys_Role</sql> //将sql主体部分抽取出来,便于其它sql的引用,便于后期统一管理维护 3 4 <select id="findAll" resultType="club.wuyu.domain.SysRole" > 5 <!--select * from sys_Role--> 6 <include refid="selectSysRole"></include> //对抽取的sql片断做引用 7 </select>
10. mybatis 核心配置文件
>> typeHandlers 标签:类型处理器; 完成数据库类型与java类型参数、结果类型的转换;
> 自定义类型转换器的开发步骤
* 实现 org.apache.ibatis.type.TypeHandler 接口;或 继承 org.apache.itatis.type.BaseTypeHandler
* 开发步骤
a. 定义转换类继承类 BaseTypeHandler<T>
b. 覆盖未实现的四个方法;
setNonNullParameter: 是 java 程序设置数据到数据库的回调方法;
getNullableResult: 是查询时,mysql的字符串类型转换成java的type类型的方法;
c. 在 mybatis的核心配置文件中进行注册;
d. 测试转换是否正确;
> Demo内容如下
a. 数据库对应的表新增字段 optime bigint
b. pojo对象,新增字段 Date optime; 同时配置 getter/setter 和 tostring;
c. 新增日期类型转换处理器
1 public class DateTypeHandler extends BaseTypeHandler<Date> { 2 @Override 3 //java类型转数据库类型 4 public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException { 5 long time = date.getTime(); 6 preparedStatement.setLong(i,time); 7 } 8 9 @Override 10 //数据库类型转java类型 11 public Date getNullableResult(ResultSet resultSet, String s) throws SQLException { 12 long aLong = resultSet.getLong(s); 13 Date date = new Date(aLong); 14 return date; 15 } 16 17 @Override 18 public Date getNullableResult(ResultSet resultSet, int i) throws SQLException { 19 long aLong = resultSet.getLong(i); 20 Date date = new Date(aLong); 21 return date; 22 } 23 24 @Override 25 public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException { 26 long aLong = callableStatement.getLong(i); 27 Date date = new Date(aLong); 28 return date; 29 } 30 }
d. mybatis 主配置文件中,对类型转换器进行登记
1 <typeHandlers> 2 <typeHandler handler="club.wuyu.handler.DateTypeHandler"></typeHandler> 3 </typeHandlers>
e. 模块配置文件中,对新加入的数据库字段进行配置
1 <!--插入操作--> 2 <insert id="save" parameterType="club.wuyu.domain.SysRole"> 3 insert into sys_Role values (#{id},#{roleName},#{roleDesc},#{optime}) 4 </insert>
f. 测试代码
1 @Test 2 public void test2() throws IOException { 3 //加载核心配置文件 4 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); 5 //获取sqlSession工厂对象 6 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 7 //通过工厂对象,获得sqlSession对象 8 SqlSession sqlSession = sessionFactory.openSession(); 9 //执行操作 10 SysRole sysRole = new SysRole(); 11 sysRole.setRoleName("测试"); 12 sysRole.setRoleDesc("测试用的一个分组"); 13 sysRole.setOptime(new Date()); 14 15 sqlSession.insert("sysRole.save",sysRole); 16 sqlSession.commit(); 17 18 //释放资源 19 sqlSession.close(); 20 }
>> plugins 标签 (以分页助手为例讲解)
> 导入通用 PageHelper 的坐标
1 <dependency> 2 <groupId>com.github.pagehelper</groupId> 3 <artifactId>pagehelper</artifactId> 4 <version>5.1.8</version> 5 </dependency> 6 <dependency> 7 <groupId>com.github.jsqlparser</groupId> 8 <artifactId>jsqlparser</artifactId> 9 <version>4.4</version> 10 </dependency>
> 在 mybatis 核心配置文件中配置 PageHelper 操作;
1 <plugins>
2 <plugin interceptor="com.github.pagehelper">
3 <property name="dialect" value="mysql"/>
4 </plugin>
5 </plugins>
>上述配置,在 5.0以上的版本中,是会报错的
org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in SQL Mapper Configuration
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration.
Cause: java.lang.ClassCastException: com.github.pagehelper.PageHelper cannot be cast to org.apache.ibatis.plugin.Interceptor
> 正确配置
1 <plugins> 2 <plugin interceptor="com.github.pagehelper.PageInterceptor"/> 3 </plugins>
> 解析错误
java.lang.NoSuchMethodError: net.sf.jsqlparser.statement.select.PlainSelect.getGroupByColumnReferences()Ljava/util/List;
> 原因:jsqlparser 版本问题;
在自己的项目中,依赖了 4.4版本的 jsqlparser;
在 5.1.8 版本中的 pagehelper 中,中依赖的是 1.2版本的 jsqlparser;
> 处理:直接使用 1.2版本的 jsqlparser 可以解决;或者直接在自己的项目中就不引用 jsqparser 了也可以解决;
经测试,可支持 1.4, 之后的任意版本2.0~4.4 都会报错
> 测试用demo
1 @Test 2 public void test1() throws IOException { 3 //加载核心配置文件 4 InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); 5 //获取sqlSession工厂对象 6 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 7 //通过工厂对象,获得sqlSession对象 8 SqlSession sqlSession = sessionFactory.openSession(); 9 10 //分页配置 11 PageHelper.startPage(2,2); 12 //执行sql语句 13 // List<SysRole> sysRoleList = sqlSession.selectList("sysRole.findAll"); 14 SysRoleMapper mapper = sqlSession.getMapper(SysRoleMapper.class); 15 List<SysRole> sysRoleList = mapper.findAll(); 16 17 //展示结果 18 for (SysRole sysRole : sysRoleList) { 19 System.out.println(sysRole); 20 } 21 22 //分页数据的获取 23 System.out.println("----------------"); 24 PageInfo<SysRole> pageInfo = new PageInfo<>(sysRoleList); 25 System.out.println(pageInfo); 26 System.out.println("----------------"); 27 28 //释放资源 29 sqlSession.close(); 30 } 31 }
> 分页信息数据如下
1 PageInfo{ 2 pageNum=2, 3 pageSize=2, 4 size=2, 5 startRow=3, 6 endRow=4, 7 total=6, 8 pages=3, 9 list=Page{ 10 count=true, 11 pageNum=2, 12 pageSize=2, 13 startRow=2, 14 endRow=4, 15 total=6, 16 pages=3, 17 reasonable=false, 18 pageSizeZero=false 19 } 20 [ 21 SysRole{id=3, roleName='讲师', roleDesc='授课工作', optime=Thu Jan 01 08:00:00 CST 1970}, 22 SysRole{id=4, roleName='助教', roleDesc='协助解决学生的问题', optime=Thu Jan 01 08:00:00 CST 1970}], 23 prePage=1, 24 nextPage=3, 25 isFirstPage=false, 26 isLastPage=false, 27 hasPreviousPage=true, 28 hasNextPage=true, 29 navigatePages=8, 30 navigateFirstPage=1, 31 navigateLastPage=3, 32 navigatepageNums=[1, 2, 3]}
> 分页数据属性
11. 数据集结果加载配置
>> 单表记录配置
1 <!--直接配置,无需特殊操作--> 2 <select id="findAll" resultType="customer"> 3 select * from Customer 4 </select>
>> 一对一关系配置 (即一个对象中的某个属性是另外一个对象; 如: 订单信息:客户信息 )
1 <resultMap id="bills" type="billinfo"> 2 <id property="id" column="bid"/> 3 <result property="billNo" column="billno"/> 4 <result property="billTime" column="billtime"/> 5 6 <association property="customer" javaType="customer"> 7 <id property="id" column="cid"/> 8 <result property="custName" column="CustName"/> 9 <result property="custPhone" column="CustPhone"/> 10 </association> 11 </resultMap>
>> 一对多或多对多关系 (即一个对象中的某个属性,是另外一个对象的集合,如: 客户信息: 订单信息)
1 <resultMap id="customerinfo" type="customer"> 2 <id property="id" column="uid"/> 3 <result property="custName" column="CustName"/> 4 <result property="custPhone" column="CustPhone"/> 5 6 <collection property="billInfos" ofType="billinfo"> 7 <id property="id" column="bid"/> 8 <result property="billNo" column="billno"/> 9 <result property="billTime" column="billtime"/> 10 </collection> 11 </resultMap>
12. mybatis 的注解开发
@Insert: 新增;
@Update : 修改;
@Delete: 删除
@Select: 查询
@Result: 查询结果
@Results : 与@Result 一起使用,封装多个结果集;
@One : 实现一对一结果集封装
@Many : 实现一对多结果集封装
>> 将模块配置文件中的信息,切换为注解模式 (模块配置文件就可以删除了)
>> 核心配置文件中的映射关系 ( mappers )的修改
> 配置模式下,mappers 下配置的是 <mapper> ,用以标识模块的配置文件;
> 注解模式下,mappers 下配置的是 <package>, 用以标识使用注解的文件所在的包; (接口所在的包)
>> 注解方式实现复杂查询
* 在配置方式下,可以通过 <resultMap> 来实现;在使用注解后,可以使用 @Results, @Result, @One, @Many 注解组合完成复杂关系的配置;
> 注解配置 1对1 或常规注解配置
> 1 对1 模式下,对应配置模式下的 association 模式对应的注解配置
** 引用对象
> 1对多 模式下的配置
* 多方.查询配置,便于引用
* 1方.查询配置
> 多对多 模式下的配置 (存在居间表)
* 角色加载配置 (按员工ID查)
* 人员信息加载配置
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律