Mybatis

一 概念

1.mybatis 是什么?

mybatis 是一个优秀的数据持久层框架。它支持自定义 SQL、存储过程和高级映射。

2.mybatis的前世今生

mybatis原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了 Google Code,随着开发团队转投Google Code 旗下, iBatis3.x正式更名为MyBatis。

 3.mybatis的好处

   mybatis是对jdbc进行轻量级的封装,提供专门xml文件来进行配置,以及可以自动的对查询结果进行封装,是一个ORM(java对象与数据库表映射)实现的数据持久层的框架,提供一些自己定义的类和接口来实现功能,支持动态sql,以及数据缓存.

Mybatis搭建

   1.创建一个maven项目,添加mybatis,mysql依赖的jar

 

 
   2.创建本地的git仓库


   3.创建一个数据库表,以及一个应的java模型类
   

  


   4.创建mybatis全局配置文件,配置数据库连接信息,配置sql映射文件

先创建xml文件


   

5.创建sql映射文件  定义一个与接口方法名相同的查询语句

 

 6.测试mybatis 

API 接口说明 

SqlSessionFactory 接口

       SqlSessionFactory封装了所有的配置信息,负责生成一个数据库连接的会话对象SqlSession对象,但创建开销比较大,所以在整个项目中只需要创建一次即可,不用销毁

SqlSession 接口

 SqlSession  创建与数据库链接会话,类似于之前的Connection,接口中封装了对数据库操作的方法 ,SqlSessionFactory中的openSession()方法用来创建一个SqlSession对象,默认无参的默认设置事物提交为false(手动提交)

Mybatis 日志

        具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的。 Mybatis 内置的日志工厂提供日志功能,具体的日志实现有以下几种方式:

SLF4J|LOG4J|JDK_LOGGINGCOMMONS_LOGGING|STDOUT_LOGGING

配置日志 STDOUT_LOGGING"/>

参数传递

 单个参数直接传递

首先我们在接口中创建一个抽象方法:

 

接下来我们在.xml文件中编写SQL语句:

然后我们直接在main方法中调用我们的抽象方法即可:

多个参数传递
使用@Param(“id”)绑定

在接口中创建一个抽象方法:

 在.xml文件中编写SQL语句:

 在main方法中调用我们的抽象方法即可:

 

增删改查

属性

1.namespace:

称为命名空间,用来将dao与Mapper进行绑定,namespace中的值要与所需dao的目录一致

2.id:

命名空间下的唯一标识符,其中的值为namespace中对应的接口中的方法名称

3.resultType:

SQL执行语句的返回值类型.

当返回值为对象或者对象的集合时,填写该对象的全限定名(包名+类名)

当返回值为字符串或者整数类型时,填写对应的类型.如: String,Intege

4.parameterType:

dao接口中方法的参数数据类型.其值为java数据类型全限定名(包名+类名)或Mybatis中定义的别名

#{} 和${}区别

#{} 占位符,是经过预编译的,编译好 SQL 语句再取值,#方式能够防止 sql 注入 #{}:select * from admin where uid=#{id} ${} 拼接符,会传入参数字符串,取值以后再去编译 SQL 语句,$方式无法防止 Sql 注入 ${} ${}:select * from admin where id= '1'

注意:MyBatis 排序时使用 order by 动态参数时需要注意,用$而不是#

代码

先编写Dao

 在写配对的mapper

结果处理

        我们在进行传统的jdbc代码进行增删改操作时,由于这些操作基本并不需要数据库的结果响应,所以相对来说比较简单.而对于查询操作,我们需要通过对ResultSet查询结果集进行处理解析后才能将其响应给客户端,而对于结果集的处理解析往往是一个非常繁杂的过程.

    我们在使用传统的jdbc进行查询操作时,每次查询之后都需要将查询结果一一进行接收,当查询大量数据时这是一个非常庞大的工程.而在Mybatis中对于查询结果集的处理提供了处理功能.

1.简单类型输出映射

简单类型的查询返回简单的基本类型,我们以查询表中的记录条数为例
1.接口中的方法:

int findAdminCount();
2.mapper.xml中的配置文件:

 <select id="findAdminCount" resultType="java.lang.Integer">
         select count(*) from admin
  </select>
3.测试类:

 package com.ffyc.mybatisdemo.test;
 ​
 import com.ffyc.mybatisdemo.dao.AdminDao;
 import com.ffyc.mybatisdemo.model.Admin;
 import com.ffyc.mybatisdemo.util.MybatisUtil;
 import org.apache.ibatis.session.SqlSession;
 import org.junit.Test;
 ​
 public class Test3 {
 ​
     @Test
     //简单类型输出映射
     public void findAdminCount() {
         SqlSession sqlSession = MybatisUtil.getSqlSession();
         AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
         int res = adminDao.findAdminCount();
         System.out.println(res);
         sqlSession.close();
     }
 }


 ​
2.对象输出映射

   此处SQL语句查询结果为一条记录,Mybatis可以实现将记录自动封装到对象中.但是,这一切都是有前提条件的.

前提条件:

1.表中的列名与对象中的属性完全一致

2.开启全局配置中的mapUnderscoreToCameLCase,以实现查询结果中的列名与对象中的属性名自动转换.这一点我们在上面的Mybatis开发环境搭建的博客中配置过

 <!--从经典的数据库命名转为java的驼峰命名-->
   <setting name="mapUnderscoreToCamelCase" value="true"/>
我们以使用id查询表中数据为例:

1.普通java实体类

 
package com.ffyc.mybatisdemo.model;
 ​
 public class Admin {
 ​
      private int id;
      private String account;
      private String password;
      private String xb;  //此处为xb,与数据库中列名不同,我们需特别注意
      private String adminPhone;
 ​
     public String getXb() {
         return xb;
     }
 ​
     public void setXb(String xb) {
         this.xb = xb;
     }
 ​
     public String getAdminPhone() {
         return adminPhone;
     }
 ​
     public void setAdminPhone(String adminPhone) {
         this.adminPhone = adminPhone;
     }
 ​
     public Admin() {
         System.out.println("Admin无参构造");
     }
 ​
     public Admin(String account, String password, String gender) {
         this.account = account;
         this.password = password;
         this.xb = gender;
     }
 ​
     public int getId() {
         return id;
     }
 ​
     public void setId(int id) {
         System.out.println("SetId");
         this.id = id;
     }
 ​
     public String getAccount() {
         return account;
     }
 ​
     public void setAccount(String account) {
         this.account = account;
     }
 ​
     public String getPassword() {
         return password;
     }
 ​
     public void setPassword(String password) {
         this.password = password;
     }
 ​
 ​
 ​
     @Override
     public String toString() {
         return "Admin{" +
                 "id=" + id +
                 ", account='" + account + '\'' +
                 ", password='" + password + '\'' +
                 ", xb='" + xb + '\'' +
                 ", adminPhone='" + adminPhone + '\'' +
                 '}';
     }
 }

2.接口中的方法:

 Admin findAdminById(int id);
3.mapper.xml中的配置文件:

<select id="findAdminById" parameterType="int" resultType="Admin">
         select * from admin where id = #{id}
     </select>
4.测试类:

 package com.ffyc.mybatisdemo.test;
 ​
 import com.ffyc.mybatisdemo.dao.AdminDao;
 import com.ffyc.mybatisdemo.model.Admin;
 import com.ffyc.mybatisdemo.util.MybatisUtil;
 import org.apache.ibatis.session.SqlSession;
 import org.junit.Test;
 ​
 /*
    结果处理
  */
 public class Test3 {
 ​
     @Test
     //对象映射
     public void findAdminById() {
         SqlSession sqlSession = MybatisUtil.getSqlSession();
         AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
         Admin admin = adminDao.findAdminById(2);
         System.out.println(admin);
         sqlSession.close();
     }
 }

3.resultMap输出映射

        resultMap可以将指定查询结果映射为普通的java对象,但是需要我们java实体类中的属性名与SQL查询结果中的列名一致.如果SQL查询结果与java实体类中的属性名不一致我们还可以通过特殊处理,使用resultMap将字段名与实体类中的属性名建立一一对应关系,从而实现将查询结果映射到普通java对象中.

首先我们需要了解resultMap中的各种属性:

resultMap属性列表:

id: resultMap的唯一标识符

type: resultMap所映射的实体对象类型

column: 字段名与属性名不一致时用来定义对应关系.column中的值为字段名

property: 字段名与属性名不一致时用来定义对应关系.property中的值为属性名

parameterType: 参数类型

3.1特殊处理定义resultMap
1.接口中的方法:

 Admin findAdminById1(int id);
2.mapper.xml文件:

 <!--特殊情况,单独处理-->
     <resultMap id="adminMap" type="Admin">
         <!--定义列名与属性名的对应关系-->
         <id column="id" property="id"></id>
         <result column="account" property="account"></result>
         <result column="password" property="password"></result>
         <result column="admin_phone" property="adminPhone"></result>
         <result column="gender" property="xb"></result>
     </resultMap>
     <select id="findAdminById1" parameterType="int" resultMap="adminMap">
          select * from admin where id = #{id}
     </select>

3.测试类:

 package com.ffyc.mybatisdemo.test;
 ​
 import com.ffyc.mybatisdemo.dao.AdminDao;
 import com.ffyc.mybatisdemo.model.Admin;
 import com.ffyc.mybatisdemo.util.MybatisUtil;
 import org.apache.ibatis.session.SqlSession;
 import org.junit.Test;
 ​
 /*
    结果处理
  */
 public class Test3 {
 ​
     @Test
     //单独使用resultMap处理
     public void findAdminById1() {
         SqlSession sqlSession = MybatisUtil.getSqlSession();
         AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
         Admin admin = adminDao.findAdminById1(2);
         System.out.println(admin);
         sqlSession.close();
     }
 }


4.使用resultMap

1.接口中的方法:

 Admin login1(Admin admin);
2.mapper,xml文件:

 <select id="login1" resultMap="adminMap" parameterType="Admin">
         select * from admin where account=#{account} and password=#{password}
 </select>
注:

此处我们引用了resultMap为上述3.1中的“adminMap”

3.测试类:

 
package com.ffyc.mybatisdemo.test;
 ​
 import com.ffyc.mybatisdemo.dao.AdminDao;
 import com.ffyc.mybatisdemo.model.Admin;
 import com.ffyc.mybatisdemo.util.MybatisUtil;
 import org.apache.ibatis.session.SqlSession;
 import org.junit.Test;
 ​
 import java.io.IOException;
 ​
 public class Test2 {
 ​
     @Test
     //使用 parameterType 参数进行类型定义
     public void login1() {
 ​
         SqlSession sqlSession = MybatisUtil.getSqlSession();
         AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
         Admin admin1 = new Admin("admin", "111",null);
         adminDao.login1(admin1);
         sqlSession.close();
     }
 ​
 }


 嵌套查询

将一个多表关联查询拆分为多次查询,先查询主表数据,然后查询关联表数据.

select:指定关联查询对象的 Mapper Statement ID 为 findDeptByID

column="dept_id":关联查询时将 dept_id 列的值传入 findDeptByID, 并将 findDeptByID 查询的结果映射到 Emp 的 dept 属性中

collection 和 association 都需要配置 select 和 column 属性,两者配置方法相同

注解方式

常用注解标签

@Insert  @Select  @Update  @Delete  @Param  

动态sql

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号,还有臭名昭著苏的SQL拼接问题。利用动态 SQL,可以彻底摆脱这种痛苦。

动态SQL标签及属性列表:

where:

       where标签解决了我们在之前SQL拼接问题中当传参为空时会出现“WHERE AND”情况,我们在传统的jdbc写法中为了解决这个问题,将“WHERE”改为了“WHERE 1=1”.使用where标签我们就再也不用遇到这种情况,where标签会自动检测如果它包含的标签中有返回值的话,它会自动插入where,再也不用我们去手动添加. 此外如果它包含的标签中返回内容为 AND 或者 OR 开头,则它会自动替换掉,十分方便.

 <select id="findStudentList2" resultType="com.ffyc.mybatisdemo.model.Student" parameterType="Student">
         select * from student
         <where>
             <if test="no!=null&amp;no!=''">
                 no = #{no}
             </if>
             <if test="name!=null&amp;name!=''">
                 name = #{name}
             </if>
         </where>
     </select>


if:

if标签通常用于WHERE语句,UPDATE语句,INSERT语句中,通过判断参数值来决定是否使用某个查询条件,判断是否更新某一个字段,判断是否插入某一个字段的值

foreach:

foreach标签主要用于构建in条件,可在SQL中对集合进行迭代.也常用到批量删除,添加操作中.

 <select id="findStudentlist1" resultType="com.ffyc.mybatisdemo.model.Student">
         select * from student where no in
         <foreach collection="list" item="no" open="(" separator="," close=")">
             #{no}
         </foreach>
     </select>


trim:
   

格式化输出,也可用来设定或忽略前后缀

 <select id="findStudentList2" resultType="com.ffyc.mybatisdemo.model.Student" parameterType="Student">
         select * from student
         <trim prefix="where" prefixOverrides="and|or">
             <if test="no!=null&amp;no!=''">
                 no = #{no}
             </if>
             <if test="name!=null&amp;name!=''">
                 name = #{name}
             </if>
         </trim>
     </select>


collection:

 collection属性的值有三个分别为: list,array,map 对应的参数类型分别为: LIst,数组,map集合

item:

表示在迭代过程中每一个元素的别名

open:

前缀

close:

后缀

separator:

分隔符,表示迭代时每个元素之间以什么分隔

<select id="findStudentlist1" resultType="com.ffyc.mybatisdemo.model.Student">
         select * from student where no in
         <foreach collection="list" item="no" open="(" separator="," close=")">
             #{no}
         </foreach>
     </select>


choose:

从多个选项中选择一个.按照顺序判断when中的条件是否成立,有一个成立则choose结束.当choose中所有的when都不成立时,则执行otherwise中的SQL.类似于java中的switch case default.

 <select id="findStudentList2" resultType="com.ffyc.mybatisdemo.model.Student" parameterType="Student">
         select * from student
         <trim prefix="where" prefixOverrides="and|or">
            <choose>
             <when test="no!=null&amp;no!=''">
                 no = #{no}
             </when>
             <otherwise>
                 name = #{name}
             </otherwise>
            </choose>
         </trim>
     </select>


set:

  没有if标签时,如果有一个参数为null,都会导致错误.当在update语句中使用if标签时,如果最后面的if没有执行,则会导致逗号多余的错误.使用SET标签可以动态的配置set关键字,和剔除追加到条件末尾的任何不相关的逗号.

<update id="updateStudent" parameterType="Student">
         update student
         <set>
             <if test="name!=null">
                 name = #{name},
             </if>
             <if test="gender!=null">
                 gender = #{gender},
             </if>
             <if test="no!=null">
                 no = #{no}
             </if>
         </set>
         where id = #{id}
     </update>

我们在上述标签介绍时在xml文件中示例了每个标签及属性的使用方法,因此在此处我们只示例一个简单查询列表的测试方法:

1.接口中的方法:

 //动态SQl
      List<Student> findStudentList2(Student student);
2.mapper.xml文件:

<select id="findStudentList2" resultType="com.ffyc.mybatisdemo.model.Student" parameterType="Student">
         select * from student
         <where>
             <if test="no!=null&amp;no!=''">
                 no = #{no}
             </if>
             <if test="name!=null&amp;name!=''">
                 name = #{name}
             </if>
         </where>
  </select>
3.测试类:

 
@Test
     //动态SQL
     public void findStudentlist2() {
         SqlSession sqlSession = MybatisUtil.getSqlSession();
         StudentDao2 studentDao2 = sqlSession.getMapper(StudentDao2.class);
         Student student = new Student();
         student.setNo(103);  //此处我们动态传入no值进行查询
         List<Student> students = studentDao2.findStudentList2(student);
         System.out.println(students);
         sqlSession.close();
     }
 

特殊符号处理

在mybatis的xml文件中经常存在一些特殊字符,比如:<     >     "     &       <> 

这些符号正常书写时,无法转义会报错,所以要对这些符号进行转义处理。

   特殊字符           转义字符

        <                     &lt;

        >                    &gt;

        "                    &quot;

        '                     &apos;

        &                     &amp;

还有一种方法,用<![CDATA[   ]]>包裹特殊字符

<if test = "id!=null">

AND <![CDATA[   id<#{id}   ]]>

</if>

<![CDATA[   ]]>是xml语法但要注意以下几点:
    1.使用动态SQL时要像if、foreach、where等标签一但被 <![CDATA[ ]]>标签包裹,将忽略xml的解析并出错
    2.<![CDATA[ ]]>标签中不可嵌套<![CDATA[ ]]>标签
    3.<![CDATA[ ]]>尽量缩小范围,以免出错
 

缓存(Cache)

作用

是为了减轻数据库的压力,提高查询性能。

原理

程序运行时,从数据库中查询到对象在使用完后不销毁,而是存储在内存(缓存)里,再次使用该数据时,直接从内存中获取,不必再从数据库中查询,减少了查询次数。而数据库中的数据存放在电脑硬盘中,如此加快了查询速度。

Mybatis 有一级缓存和二级缓存。
一级缓存

是sqlSession级别的,在同一个sqlSession中执行两次相同的sql语句,第一次执行完毕会将查询到的数据写入缓存中,第二次执行直接从缓存中获取数据。当sqlSession结束后,该sqlSession中的一级缓存也就不存在了(sqlSession.close();),还可以通过sqlSession.clearCache();直接清空缓存,其次SqlSession 中执行了任何一个 update 操作(update()、delete()、 insert()) ,都会清空缓存的数据。一般Mybatis默认一级缓存。

二级缓存

是sqlSessionFactory级别的,将查询到的数据存放到二级缓存就可实现多个sqlSession共享,第一次查询到数据后,关闭sqlSession时数据存入二级缓存中。

二级缓存需要配置

第一步:启用二级缓存 在 SqlMapperConfig.xml 中启用二级缓存,如下代码所示,当 cacheEnabled 设置为 true 时启用二级缓存,设置为 false 时禁用二级缓存。

<setting name="cacheEnabled" value="true"/>

第二步:对象序列化 将所有的 POJO 类实现序列化接口 Java.io. Serializable。

第三步:配置映射文件 在 Mapper 映射文件中添加<cache/>,表示此 mapper 开启二级缓存。 当 SqlSeesion 关闭时,会将数据存入到二级缓存,还可根据自己所需添加其他限制如:<cache/  size = "这个是容量" flushIntervd = "这个是缓存保存时间" />

反射

概念

Java反射机制是运行程序时,可以对任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它任意的方法和属性;这种动态获取信息及动态调用对象方法的功能称为Java语言的反射机制。简而言之,可以动态获取任意类信息,以及创建类的对象,以及调用对象的属性和方法。

作用:

动态获取类的信息。

好处:写一个方法可以处理任何类。

相关API

Class类型  Constructor 构造方法  Method  方法     Field  属性,除了Class外,其他都位于java.lang.reflect包中。API将类的类型,方法,属性都封装成了类

Class类

Class类是Java反射机制的基础,通过Class类,可以得到一个类的基本信息。一旦class文件被加载到内存,就会为其创建一个Class对象。任何类被使用时都会创建一个Class对象。

获取Class类的方法:

1.Object类中的getClass方法:适用于通过对象获得Class实例的情况

2.类名.class方式:适用于通过类名获得Class实例的情况

3.Class类的静态方法 forName(String name),通过类的地址加载类,获得到类的Class对象

获得Constructor类实例

Constructor对象通过Class对象获得,Class类中定义了下面的方法:

Constructor getConstructor(Class... parameterTypes)

通过指定参数类型,返回构造方法实例。

获得构造方法

Constructor类可以通过getXXX方法获得构造方法的基本信息,例如:

可以拿到类中的公共的无参构造方法;

 可以拿到类中的公共的有参构造方法;

 可以拿到类中的公共的或私有的无参构造方法

可以拿到类中的公共的或私有的有参构造方法

创建实例

除了获得构造方法的基本信息,还可以创建实例

例如:

这是通过公共权限的构造方法创建的对象,但是私有的构造方法有点需要注意,需要设置对私有权限构造方法进行操作后,才可创建对象,如下:

 获得Field实例
Field类

将类的属性进行封装可以获得属性的基本信息、属性的值,也可以对属性进行赋值.

 获得类中属性:
 getField(name) 获得的是指定名称公共权限属性
 getFields() 获得所有公共权限属性
 getDeclaredField("name");获得的是指定名称私有权限属性
 getDeclaredFields();获得所有属性

getName:返回属性名

set:设置属性值

获得Method实例

Method实例都也是通过Class类的方法获得, 

c.getMethods()获得类中所有的公共的成员方法
getMethod("getId",type) 获得公共的指定名称参数的成员方法
c.getDeclaredFields();得类中所有的成员方法(包含私有的)
c.getDeclaredMethod(name,type)

了动态获得方法信息外,Method还能动态调用某一个对象的具体方法invoke(为以后动态代理做铺垫)即调用方法。设值还是取值要看具体的方法

method.invoke(Object obj,Object... args)

反射案例:自定义java对象转json工具类

 

反射优缺点

 优点

可以在运行时动态获取任意一个类的信息,创建对象,调用对象的方法、属性,提高代码的灵活性,复用性。

缺点

需要对类信息进行解析判断,效率低;使用反射机制可以操作私有信息,会打破封装性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值