(1)-MyBatis基础-----入门篇

MyBatis

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

其前身为apache的ibatis后来迁移到Gihub并更名为MyBatis

MyBatis特点:

1.轻量级,自身不依赖其他任何JAR,但需要提供JDBC实现

​2.灵活,更加适用于需求变化频繁的互联网应用

​3.学习成本低,相比ORM框架而言,掌握MyBatis的使用是很轻松的

在结构中的位置

(1)-MyBatis基础-----入门篇_第1张图片
可以看出MyBatis处在DAO(数据访问对象)的位置,处于模型层,用来处理数据库操作的
回顾一下

DAO的工作职责

:

  • 连接数据库

  • 接收输入数据

  • ​拼接并执行SQL

  • ​解析并返回结果

为什么需要MyBatis

使用JDBC完成DAO层存在以下问题

  • 每次操作都需要手动的创建连接,最后关闭连接

    • 对于重复代码通常开发者都会进行封装,但是由于每个人的编码风格不同导致封装的代码也没有固定的套路

    • MyBatis将数据库连接相关的参数放到配置XML中并封装了创建连接的代码

  • 频繁的创建和销毁连接

    • 由于数据库连接使用的是TCP长连接,并发量大的系统中,这样的方式会导致数据库连接资源耗尽

    • MyBatis本身实现了连接池,可以解决这一问题,当然后续会更换其他更好的连接池

  • 接受参数拼接SQL语句并执行

    • 每一条SQL语句都是直接写在代码中(硬编码),如果后期需求发生变化,则需要修改源码中的SQL,然后重新编译,测试…

    • MyBatis将SQL语句从代码中剥离到Mapper.xml映射文件中

  • 解析结果

    • JDBC返回的是ResultSet,必须手动将其映射到一个个的对象中,同样是重复度很高的代码;并且存在硬编码问题

    • MyBatis实现了入参映射到SQL参数,以及结果集映射到POJO对象

更多功能

MyBatis在解决上述问题的同时提供了更多实用的功能

  • 动态SQL,即在SQL语句中可以包含逻辑处理(判断,循环等…)
  • 高级映射,支持一对一,一对多映射
  • 动态代理Mapper,使得可以用面向对象的方式来完成数据库操作
  • 逆向工程,根据表结构自动生成,POJO,Mapper映射和Mapper接口,包含简单的CRUD

MyBatis构架

(1)-MyBatis基础-----入门篇_第2张图片

  • SqlMapConfig.xml作为全局配置,

    • 指定MyBatis的基本参数,如运行环境(开发,发布),事务管理器,数据来源等;
    • 以及需要加载的mapper映射文件(从源码中剥离出来的SQL语句)
  • SqlSessionFactory,负责读取SqlMapConfig中的参数创建会话

  • SqlSession,通过SqlSessionFactory获取一个Session(会话)

  • Executor 真正负责执行sql语句的对象

  • MappedStatement用于将输入参数映射到sql语句,以及结果集映射到POJO

上述构架中,SqlSession以下的部分是MyBatis封装好的,SqlSession负责调用它们完成操作; 开发过程中不需要涉及(特殊需求除外);

另外SqlSessionFactory和SqlSession也可以通过简单的代码获取到,后续Spring框架能够自动创建它们

所以使用MyBatis的重点就落在了SqlMapConfig.xml以及Mapper.xml中

CRUD入门

环境搭建

官方文档:https://mybatis.org/mybatis-3/getting-started.html

1. 创建项目

这里采用Maven来引入MyBatis,(详见maven笔记之入土之路)项目采用普通的Java项目,不使用Maven骨架

2. 在pom.xml中添加依赖

<dependencies>
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version>
    </dependency>
    <dependency>
<groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.17</version>
    </dependency>
    <dependency>
<groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version>
    </dependency>
    <dependency>
<groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version>
    </dependency>
</dependencies>

3.提供配置文件

3.1MyBatis全局配置

mybatis-config.xml

<?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"/>
<!--            指定数据源 就是数据来自哪里 这里默认使用MyBatis自带的连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--                //表示本机 localhost  &就是&  xml中&需要转译-->
                <property name="url" value="jdbc:mysql:///mybatisDB?serverTimezone=Asia/Shanghai&characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="admin"/>
            </dataSource>
        </environment>
    </environments>
<!--    指定要加载的映射文件-->
    <mappers>
        <mapper resource="mapper/ProductsMapper.xml"/>
    </mappers>
</configuration>

3.2 log4日志模块,定义输出格式的配置
log4j.properties

#Global logging configuration
log4j.rootLogger=DEBUG, stdout  
#Console output... 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

这里先书写

第一个MyBatis案例根据id查询

3.3 创建实体类以及对应表

CREATE DATABASE IF NOT EXISTS `mybatisDB` CHARACTER SET utf8;
USE `mybatisDB`;
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
 `pid` int(11) NOT NULL AUTO_INCREMENT,
 `pname` varchar(20) DEFAULT NULL,
 `price` double DEFAULT NULL,
 `pdate` date DEFAULT NULL,
 `cid` varchar(20) DEFAULT NULL,
 PRIMARY KEY (`pid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
INSERT INTO `products` VALUES (2,'新疆⼤枣',38,NULL,'s002'),(3,'新疆切
糕',68,NULL,'s001'),(4,'⼗三⾹',10,NULL,'s002'),(5,'⽼⼲爹',20,NULL,'s002'),(6,'泰
国咖喱',50.5,'2020-01-02','s001'),(7,'泰国咖喱2',50.5,'2020-01-02','s001');

得到数据库表(1)-MyBatis基础-----入门篇_第3张图片
pojo实体类

package com.cx.pojo;

import java.util.Date;

/**
 *product实体类
 */
public class Product {
    private Integer pid;
    private String pname;
    private Double price;
    private Date pdate;
    private String cid;

    public Product() {
    }

    public Product(Integer pid, String pname, Double price, Date pdate, String cid) {
        this.pid = pid;
        this.pname = pname;
        this.price = price;
        this.pdate = pdate;
        this.cid = cid;
    }

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public Date getPdate() {
        return pdate;
    }

    public void setPdate(Date pdate) {
        this.pdate = pdate;
    }

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    @Override
    public String toString() {
        return "Product{" +
                "pid=" + pid +
                ", pname='" + pname + '\'' +
                ", price=" + price +
                ", pdate=" + pdate +
                ", cid='" + cid + '\'' +
                '}';
    }
}

3.4创建mapper.xml,直接操作数据库,编写sql语句
ProductsMapper.xml

<?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 用于多个Mapper出现相同的sql时区分不同包-->
<mapper namespace="com.cx.pojo.Product">

		<!--查询语句
    id用于表示这条sql
    
    parameterType 表示 sql语句接受一个整数参数
    
    resultType表示 将结果映射到Products对象中
    
    #{} 表示一个站位符等同于 ?
            若输入参数是基础数据类型则可以随意写
            若输入是一个POJO则写属性名称-->
    <select id="selectProductBtId" parameterType="int" resultType="com.yyh.pojo.Products">
        select *from products where pid = #{pid}
    </select>
  
</mapper>

注意: #{} 表示⼀个站位符等同于 ?,可以用来取值,若输⼊参数是基础数据类型则可以随意写,若输⼊是⼀个POJO则写属性名称,但是如何是在字符串中就不能用了,而要用KaTeX parse error: Expected 'EOF', got '#' at position 12: {} 如:pid = #̲{pid} ,pid li…{pid}%”

parameterType 输入映射 表示 sql语句接受的参数类型,参数在sql语句中直接通过 #{} 或${} 取。

resultType 输出映射,表示将结果映射到设定的类型中
不要忘记将这个mapper配置到mybatis-config.xml中

 <mappers>
        <mapper resource="mapper/ProductsMapper.xml"/>
 </mappers>

3.4书写测试代码

import com.cx.pojo.Product;
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 org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

/**
 *mybatis入门案例
 */
public class MyTest {
    @Test
    public void selectById() throws IOException 
     //1.加载核心配置文件mybatis-config.xml
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    //2.创建sqlsessionFactoryBuilder对象
    SqlSessionFactoryBuilder sqlsessionFactoryBuilder = new SqlSessionFactoryBuilder();
    //3.读取配置文件的流,创建sqlSessionFactory会话 工厂
    SqlSessionFactory sqlSessionFactory = sqlsessionFactoryBuilder.build(inputStream);
    //4.创建Session对象
    SqlSession session = sqlSessionFactory.openSession();
    //5.执行查询语句
    Product product = session.selectOne("selectById",3);

    System.out.println(product);
    session.close();
    }
}

点击test输出结果:
在这里插入图片描述
在这里,出现了maven点击test时出现乱码:如图
在这里插入图片描述
解决办法见IDEA下使用Maven的test命令乱码解决方案
那么第一个案例就书写完毕。selectOne只能是有两个参数,一个是sql的id,一个是要传递的参数。
多个参数用map
总结一下步骤:

  1. 在pom.xml中引入相关jar包
  2. 创建全局配置文件,编写数据库参数
  3. 创建实体类,数据库对应表
  4. 编写对应mapper文件
  5. 加载核心配置文件mybatis-config.xml
  6. 创建SqlSessionFactoryBuilder对象,通过.build方法创建会话工厂,读取流
  7. sqlSessionFactory.openSession创建sqlSession对象,执行sql语句。

案例2:模糊查询

在ProductsMapper.xml增加标签

<select id="selectProductLikeName" parameterType="string" resultType="com.yyh.pojo.Product">
  select *from products where pname like "%${pname}%"
</select>

测试代码:由于多个测试方法都需要工厂所以讲工厂作为属性并在,@Before中进行初始化,因为每次都用的
此处不建议session也放在此处,因为session不是线程安全的对象,所以不建议多个地方使用同一个。

private SqlSessionFactory sqlSessionFactory = null;
    @Before
    public void before() throws IOException {
         //1.加载核心配置文件mybatis-config.xml
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    //2.创建sqlsessionFactoryBuilder对象
    SqlSessionFactoryBuilder sqlsessionFactoryBuilder = new SqlSessionFactoryBuilder();
    //3.读取配置文件的流,创建sqlSessionFactory会话 工厂
    sqlSessionFactory = sqlsessionFactoryBuilder.build(inputStream);
    }

所以测试代码:

 @Test
    public void selectProductLikeName(){
        //4.创建Session对象
    SqlSession session = sqlSessionFactory.openSession();
        //5.执行查询语句
        List<Product> product = session.selectList("selectProductLikeName","新");
        System.out.println(product);
        session.close();
    }

输出结果:
在这里插入图片描述
需要注意的是:在mapper中当需要做将参数进行字符串拼接时则不能在使用#{} 需更换为${}表示将参数结果提取并拼接到字符串中

注意了:若MyBatis版本低于3.5.2 则在字符串中使用#{xxx}的方式将导致找不到属性异常,在低版本中MyBatis将#{xxx}中的xxx当做参数的属性去参数中查找get方法(很明显找不到),在字符串内和字符串外获取属性的行为竟然不同,好在3.5.2解决了这个问题

案例3:sql中需要多个参数用map

在ProductsMapper.xml增加标签

<select id="selectByNameAndPrice" parameterType="map" resultType="com.cx.pojo.Product">
     select * from product where pname=#{pname} and price = #{price}
</select>

书写测试代码

@Test
    public void selectByNameAndPrice(){
         //4.创建Session对象
    SqlSession session = sqlSessionFactory.openSession();
        Map<String,Object> map = new HashMap<>();
        map.put("pname","⼗三⾹");
        map.put("price",10);
        //5.执行查询语句
        Product product = session.selectOne("selectByNameAndPrice",map);
        System.out.println(product);
        session.close();
    }

运行结果
在这里插入图片描述

案例4:插入数据

在ProductsMapper.xml增加标签

 <insert id="insertProduct" parameterType="com.cx.pojo.Product">
        insert into product values (null,#{pname},#{price},#{pdate},#{cid})
 </insert>

书写测试代码

@Test
    public void insertProduct(){
        //设置自动提交为true  默认为false
        //4.创建Session对象
    SqlSession session = sqlSessionFactory.openSession();
        Product product = new Product();
        product.setPrice(100d);
        product.setPname("粮食2789");
        product.setPdate(new Date());
        product.setCid("2222");
        //5.执行插入语
        int o = session.insert("insertProduct", product);
        session.commit();//手动提交
        System.out.println(o);
        session.close();
    }

运行结果:
在这里插入图片描述
强调:
当sql为update insert delete时需要commit才会生效,SqlSession默认不自动提交的,可以在获取SqlSession时指定自动提交,也可以在执行完后手动调用commit;

sql为update insert delete时返回值为受影响行数

获取插入数据的id

很多情况下需要获取刚刚添加的记录的id用来做表关联,要获得id有两种方式

4.1对于支持自增的数据库MySQL,下述案例:MyBatis会把id存储到传入对象的pid属性中

 <insert id="insertProduct" parameterType="com.cx.pojo.Product" keyProperty="pid" useGeneratedKeys="true">
        insert into product values (null,#{pname},#{price},#{pdate},#{cid})
 </insert>

4.2兼容不支持自增的数据库

 <insert id="insertProduct" parameterType="com.cx.pojo.Product">
        insert into product values (null,#{pname},#{price},#{pdate},#{cid})
        <!--指定如何获取id 并放入对象某个属性中 -->
        <selectKey resultType="int" keyProperty="pid" order="AFTER" >
            select last_insert_id();
        </selectKey>
 </insert>

注意:select last_insert_id();是mysql的函数,oracle没有,那就需要将其替换为oracle中生产id的函数,selectKey的原理是执行这个sql函数,然后将结果放入对象的属性中,order指定id放入对象属性是在执行sql前或者后

关于before和after的选择,要根据id的来源是自增的还是自己编写语句获取的,如下:
(1)-MyBatis基础-----入门篇_第4张图片

案例5:更新数据

在ProductsMapper.xml增加标签

<update id="updateProduct" parameterType="com.cx.pojo.Product">
        update product set
            pname=#{pname},
            price=#{price},
            pdate=#{pdate},
            cid=#{cid}
        where pid=#{pid}
    </update>

书写测试代码

@Test
    public void updateProduct(){
        //设置自动提交为true 默认为false
        //4.创建Session对象
    SqlSession session = sqlSessionFactory.openSession();
        Product product = session.selectOne("selectById", 3);
        product.setPrice(10d);
        product.setPname("哈密瓜");
        product.setPdate(new Date());
        product.setCid("s10022");
        //5.执行更新语
        int o = session.update("updateProduct", product);
        session.commit();//手动提交
        System.out.println(o);
        session.close();
    }

运行结果:
在这里插入图片描述

案例6:删除案例

在ProductsMapper.xml增加标签

<delete id="deleteById" parameterType="int">
        delete from product where pid = #{pid}
    </delete>

书写测试代码

 @Test
    public void deleteProduct(){
        //4.创建Session对象
    SqlSession session = sqlSessionFactory.openSession();
        //5.执行删除语
        int o = session.delete("deleteById", 3);
        session.commit();//手动提交
        System.out.println(o);
        session.close();
    }

下一篇:MyBatis进阶----进阶篇

你可能感兴趣的:(MyBatis)