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查)

        

 

      * 人员信息加载配置

        

 

 

 

        

 

 

 

      

 

 

 

 

 

 

 

 

 

 

posted @ 2022-06-16 10:23  耗喜天涯  阅读(70)  评论(0编辑  收藏  举报