MyBatis

为什么使用框架
框架( Framework )是一个框子 —— 指其约束性,也是一个架子 —— 指其支撑性。是一个基本概念上的结构,用于去解决或者处理复杂的问题。框架这个广泛的定义使用的十分流行,尤其在软件概念。
框架 (Framework) 是构成一类特定软件可复用设计的一组相互协作的类。框架规定了你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中精力于应用本身的特定细节。如果将开发完成的软件比作是一套已经装修完毕的新房,那框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,建筑质量和户型合理有保证,还省去了自己建造房屋的时间,一举多得。在开发过程是使用框架,同样可以保证减少开发时间、降低开发难度,并且还保证设计质量。好比和世界上最优秀的软件工程师是一个项目的,并且他们完成的还是基础、全局的工作。框架还有一个作用是约束。莎士比亚说," 一千个观众眼中有一千个哈姆雷特 " 即仁者见仁 , 智者见智 . 说每个人都会对作品有不同的理解,每个人对待任何事物都有自己的看法,一千个人就有可能有一千种不同的看法1000 人心中有 1000 个哈姆雷特。同样的技术解决同样的问题会产生不同流程和风格的解决方案,而采用一种框架其实就是限制用户必须使用其规定的方案来实现,可以降低程序员之间沟通以及日后维护的成本。总之,框架是一个半成品,已经对基础的代码进行了封装并提供相应的API ,开发者在使用框架是直接调用封装好的 API 可以省去很多代码编写,从而提高工作效率和开发速度。

世界上流行的框架

Spring

play

Hibernate

MyBatis

Struts等

ORM

 ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法。

MyBatis

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。

MyBatis 是一个半自动化的ORM框架

持久化

持久化

  • 持久化是将程序数据在持久状态和瞬时状态间转换的机制。

    • 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
    • JDBC就是一种持久化机制。文件IO也是一种持久化机制。
    • 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。
  • 为什么需要持久化服务呢?那是由于内存本身的缺陷引起的

    • 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
    • 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。

瞬时状态

当创建一个对象后,但是没有存储到数据库中之前的这一段时间状态被称为瞬时态。

事务

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书用户程序的执行所引起,并用形如begin transactionend transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

默认情况下, MySQL 的事务提交方式是自动提交。每一条 SQL 语句就是一个独立的事务!

延迟加载

  1. 概念:MyBatis中的延迟加载也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫做按需查询。例如,在进行一对多查询的时候,只查询出一方,当程序中需要多方数据时,MyBatis在发出sql语句进行查询,这样子延迟加载就可以减少数据库压力,MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。

  2. MyBatis中延迟加载的条件:resultMap可以实现高级映射,例:使用association、collection实现一对一及一对多映射,association、collection具备延迟加载功能。

  3. 延迟加载的好处:先从单表查询,需要时再从关联表查询,大大提高数据库性能,因为查询单表比关联查询多张表速度要快。

  4. 使用场景:在对应的四种关系表中,一对多、多对多通常情况下采用延迟加载,多对一、一对一、通常采用立即加载。

  5. 延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询,因为多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询,会一次性将多张表的所有信息查询出来。

MyBatis环境搭建

下载地址
https://github.com/mybatis/mybatis-3/releases
中文文档
https://mybatis.net.cn/

导入依赖

<dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
</dependencies>

全局配置文件

<?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>
<!--  读取属性文件  -->
    <properties resource="jdbc.properties"/>
<!--    给映射起别名-->
    <typeAliases>
<!--        给某一个类做别名-->
<!--        <typeAlias type="com.csi.smbms.domain.User" alias="User"></typeAlias>-->
<!--        扫描包,将包中所有类全部自动化命名,规则:首字母大写-->
        <package name="com.csi.smbms.domain"/>
    </typeAliases>
    <!--default代表设置默认指向的数据库-->
    <environments default="development">
        <!--配置环境,不同环境不同的id名字-->
        <!--development开发环境环境-->
        <environment id="development">
            <!--采用JDBC方式对数据库事务进行commit/rollback-->
            <transactionManager type="JDBC"></transactionManager>
            <!--采用连接池方式管理数据库-->
            <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>
        <environment id="test">
            <!--采用JDBC方式对数据库事务进行commit/rollback-->
            <transactionManager type="JDBC"></transactionManager>
            <!--采用连接池方式管理数据库-->
            <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/csi/smbms/domain/UserMapper.xml"/>
        <mapper resource="com/csi/smbms/domain/ProviderMapper.xml"/>
        <mapper resource="com/csi/smbms/domain/BillMapper.xml"/>
    </mappers>
</configuration>
typeAliaes
配置别名:
                                分别配置
                                使用包扫描
                                在没有注解的情况下,会使用 JAVABean 的首字母小写的非限定类名来作为它的别名。

属性配置文件

driver = com.mysql.cj.jdbc.Driver
url = jdbc : mysql : //192.168.110.249 : 3306/smbms
username = root
password = root
要将 dao 接口类与 mapper 映射文件放置在同一个包中。

映射文件

<?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代表命名空间,在mybatis的作用域内,必须保持唯一性,一般都是包名加类名
    select:
            id:代表方法的名称
            resultType:返回值类型
            节点中间的文本内容就是一条SQL语句
-->
<!--这里所对应的位置必须是接口的全类名
    接口中的方法必须是当前的id名称
    方法返回值必须一致
    #{}:传统占位符?
    ${}:statement的拼接,字符串拼接,可能会出现SQL注入问题
-->
<mapper namespace="com.csi.smbms.dao.UserMapper">
    <!--这个id是dao里的方法名    返回类型-->



<!--  用户登录功能  -->
    <select id="login" resultType="com.csi.smbms.domain.User" parameterType="String">
        select * from smbms_user where userCode = #{arg0} and  userPassword=#{arg1}
    </select>

    <select id="list" resultType="com.csi.smbms.domain.User">
        select * from smbms_user
    </select>
    <select id="findByLikeUserName" resultType="com.csi.smbms.domain.User">
        select * from smbms_user where userName LIKE CONCAT('%',#{userName},'%')
    </select>
    <select id="findByLikeCreationDate" resultType="com.csi.smbms.domain.User">
        select * from smbms_user where creationDate LIKE CONCAT('%',#{creationDate},'%')
    </select>
    <insert id="save" parameterType="com.csi.smbms.domain.User">
        insert into smbms_user(userCode,userName,userPassword,gender,birthday,phone,address,userRole,createdBy,creationDate)
        values (#{userCode},#{userName},#{userPassword},#{gender},#{birthday},#{phone},#{address},#{userRole},#{createdBy},now());
    </insert>
    <update id="update" parameterType="com.csi.smbms.domain.User">
        update smbms_user set  userName = #{userName} ,userCode = #{userCode}, userPassword=#{userPassword} ,gender=#{gender} , birthday=#{birthday}
        ,phone=#{phone},address=#{address},modifyBy=#{modifyBy}, modifyDate=now()

        where id=#{id}
    </update>
    <delete id="delete" >
        delete from smbms_user where id = ${id};
    </delete>












    <select id="findByConditions" parameterType="User">
        select * from smbms_user where userCode = #{userCode} and  userRole = #{userRole}
    </select>



<!--    select * from smbms_user where userName LIKE CONCAT('%',#{userName},'%')    $和#的区别面试题-->
    <select id="findByLike" resultType="User" parameterType="String">
        select * from smbms_user where userName LIKE '%${userName}%'

    </select>
    <select id="findById" resultType="com.csi.smbms.domain.User" parameterType="long">
        select * from smbms_user where id = #{id}
    </select>
    <select id="findByCount" resultType="int">
        select count(*) from smbms_user
    </select>

</mapper>

Mapper接口形式操作

接口名称需要与映射配置文件名称相同
映射配置文件中 namespace 必须是接口的全名。
接口中的方法名和映射配置文件中的标签的 id 一致。
接口中的返回值类型和映射配置文件中的 resultType 的指定的类型一致。
声明一个接口,将接口与对应的映射文件放置在同一个文件夹中, namespace 配置需要引用接口名称,接口中的方法名称与映射文件
中的节点 id 一致,参数类型、返回值类型一致,在调用时,使用 SqlSession 对象的 getMapper(Mapper.class) 的形式调用。
Mapper 接口形式的原理
Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,就是映射文件中 Mapper 的Statement的 id 值;接口方法内的参数,就是传递给 sql 的参数。 Mapper 接口是没有实现类的,当调用接口方法时,接口全限名 + 方法名拼接字符串作为 key 值,可唯一定位一个MapperStatement。

映射类型

 

日志配置文件

log4j.rootLogger=DEBUG,CONSOLE,file #log4j.rootLogger=ERROR,ROLLING_FILE log4j.logger.com.csi.dao=debug log4j.logger.com.ibatis=debug log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug log4j.logger.java.sql.Connection=debug log4j.logger.java.sql.Statement=debug log4j.logger.java.sql.PreparedStatement=debug log4j.logger.java.sql.ResultSet=debug log4j.logger.org.tuckey.web.filters.urlrewrite.UrlRewriteFilter=debug ###################################################################################### # Console Appender \u65e5\u5fd7\u5728\u63a7\u5236\u8f93\u51fa\u914d\u7f6e ###################################################################################### log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.Threshold=error log4j.appender.CONSOLE.Target=System.out log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern= [%p] %d %c - %m%n ###################################################################################### # DailyRolling File \u6bcf\u5929\u4ea7\u751f\u4e00\u4e2a\u65e5\u5fd7\u6587\u4ef6\uff0c\u6587\u4ef6\u540d\u683c\u5f0f:log2009-09-11 ###################################################################################### log4j.appender.file=org.apache.log4j.DailyRollingFileAppender log4j.appender.file.DatePattern=yyyy-MM-dd log4j.appender.file.File=log.log log4j.appender.file.Append=true log4j.appender.file.Threshold=error log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n

MyBatis的生命周期

SqlSessionFactoryBuilder
用过即丢,其生命周期只存在于方法体内
可重用其来创建多个 SqlSessionFactory 实例
负责构建 SqlSessionFactory ,并提供多个 build 方法的重载
SqlSessionFactory
在创建 SqlSession 的同时,能够将事务设置为手动的或者是自动的
作用域: Application
生命周期与应用的生命周期相同
单例
存在于整个应用运行时,并且同时只存在一个对象实例
SqlSession
包含了执行 SQL 所需的所有方法
对应一次数据库会话,会话结束必须关闭
线程级别,不能共享

MyBatisUtils

package com.csi.smbms.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 factory;

    private static ThreadLocal<SqlSession>  tl = new ThreadLocal<>();

    static {
        System.out.println("工具类初始化");
        //步骤1:读取配置文件,读,用字节流
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }

        //步骤2:通过API对应的功能实现数据库连接    后面的是多数据库切换括号里+“”
        factory = new SqlSessionFactoryBuilder().build(is);

    }

    /**
     * SqlSession创建问题
     * @return
     */
    public static SqlSession getSqlSession(){
        //先查一下本地有没有
        SqlSession sqlSession = tl.get();
        //判断本地线程中是否存在sqlsession 不存在则构造一个新的sqlsession 同时放在ThreadLocal中
        if (sqlSession==null){
            System.out.println("创建");
            sqlSession = factory.openSession();
            //没有存放过
            tl.set(sqlSession);
        }
        //不为空
        return sqlSession;
    }

    /**
     * 关闭
     */

    public static void close(){
        SqlSession sqlSession = tl.get();
        if (sqlSession!=null){
            System.out.println("关闭");
            sqlSession.close();
            //不要把ThreadLocal整个对象全关了,将其置空
            tl.set(null);
        }
    }

}

操作

 查询

List: SelectList(namespace+id, 参数内容 )
SelectOne: 查询一个, SelectOne(namespace+id, 参数内容 ) ;

模糊查询

使用字符串拼接形式 '%${ 变量名称 }%'
使用 concat 方式 concat('%',#{ 变量名称 },'%')
<select id="findByLikeCreationDate" resultType="com.csi.smbms.domain.User">
        select * from smbms_user where creationDate LIKE CONCAT('%',#{creationDate},'%')
    </select>

测试类

 @Test
    public void testBillConcatSelect(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        BillMapper billMapper = sqlSession.getMapper(BillMapper.class);
        Bill bill = billMapper.selectIsPayment("大豆油", 2);
        System.out.println(bill);
        MyBatisUtils.close();
    }

map方式

xml

List < Dept > findByCondition ( Map < String , Object > map ) ;

<select id = "findByCondition" resultType = "dept" parameterType = "map" >
SELECT * FROM dept WHERE dname = #{dname} and loc = #{loc}
</select>
当查询的条件都是来自于同一张表,在前端,通过类似于 Spring MVC 的框架,能够自动实现数据的封装,此时利用对象封装参数查询就比较方便。
当查询的条件都是来自于不同的表内容,那么此时,要么新建立一个临时对象用于作为查询的参数,最好的形式就是 map 来实现该功能。

 $#之间的区别(**)

#{}:传统占位符?,可以有效的防止SQL注入,提高系统安全性。 
${}:statement的拼接,字符串拼接,可能会出现SQL注入问题

#{}是预编译处理,${}是字符串替换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值