MyBatis

框架

三层架构

JDBC不足

// ......
// sql语句写死在java程序中
String sql = "insert into t_user(id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade) values(?,?,?,?,?,?,?,?,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// 繁琐的赋值:思考一下,这种有规律的代码能不能通过反射机制来做自动化。
ps.setString(1, "1");
ps.setString(2, "123456789");
ps.setString(3, "zhangsan");
ps.setString(4, "123456");
ps.setString(5, "1980-10-11");
ps.setString(6, "男");
ps.setString(7, "zhangsan@126.com");
ps.setString(8, "北京");
ps.setString(9, "大兴区凉水河二街");
ps.setString(10, "1000000");
ps.setString(11, "16398574152");
ps.setString(12, "A");
// 执行SQL
int count = ps.executeUpdate();
// ......
// ......
// sql语句写死在java程序中
String sql = "select id,idCard,username,password,birth,gender,email,city,street,zipcode,phone,grade from t_user";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
List<User> userList = new ArrayList<>();
// 思考以下循环中的所有代码是否可以使用反射进行自动化封装。
while(rs.next()){
    // 获取数据
    String id = rs.getString("id");
    String idCard = rs.getString("idCard");
    String username = rs.getString("username");
    String password = rs.getString("password");
    String birth = rs.getString("birth");
    String gender = rs.getString("gender");
    String email = rs.getString("email");
    String city = rs.getString("city");
    String street = rs.getString("street");
    String zipcode = rs.getString("zipcode");
    String phone = rs.getString("phone");
    String grade = rs.getString("grade");
    // 创建对象
    User user = new User();
    // 给对象属性赋值
    user.setId(id);
    user.setIdCard(idCard);
    user.setUsername(username);
    user.setPassword(password);
    user.setBirth(birth);
    user.setGender(gender);
    user.setEmail(email);
    user.setCity(city);
    user.setStreet(street);
    user.setZipcode(zipcode);
    user.setPhone(phone);
    user.setGrade(grade);
    // 添加到集合
    userList.add(user);
}
// ......

了解MyBatis

入门案例开发

readme.txt

开发我的第一个MyBatis程序

1. resources目录:
    放在这个目录当中的,一般都是资源文件,配置文件。
    直接放到resources目录下的资源,等同于放到了类
    的根路径下。

2. 开发步骤
* 第一步:打包方式jar
* 第二步:引入依赖
    - mybatis依赖
    - mysql驱动依赖
* 第三步:编写mybatis核心配置文件:mybatis-config.xml
    注意:
        第一:这个文件名不是必须叫做mybatis-config.xml,可以用其他的名字。只是大家都采用这个名字。
        第二:这个文件存放的位置也不是固定的,可以随意,但一般情况下,会放到类的根路径下。

    mybatis-config.xml文件中的配置信息不理解没关系,先把连接数据库的信息修改以下即可。
    其他的别动。
* 第四步:编写XxxxMapper.xml文件
    在这个配置文件当中编写SQL语句。
    这个文件名也不是固定的,放的位置也不是固定,我们这里给它起个名字,叫做:CarMapper.xml
    把它暂时放到类的根路径下。
* 第五步:在mybatis-config.xml文件中指定XxxxMapper.xml文件的路径:
    <mapper resource="CarMapper.xml"/>
    注意:resource属性会自动从类的根路径下开始查找资源。

* 第六步:编写MyBatis程序。(使用mybatis的类库,编写mybatis程序,连接数据库,做增删改查就行了。)
    在MyBatis当中,负责执行SQL语句的那个对象叫做什么呢?
        SqlSession
    SqlSession是专门用来执行SQL语句的,是一个Java程序和数据库之间的一次会话。
    要想获取SqlSession对象,需要先获取SqlSessionFactory对象,通过SqlSessionFactory工厂来生产SqlSession对象。
    怎么获取SqlSessionFactory对象呢?
        需要首先获取SqlSessionFactoryBuilder对象。
        通过SqlSessionFactoryBuilder对象的build方法,来获取一个SqlSessionFactory对象。

    mybatis的核心对象包括:
        SqlSessionFactoryBuilder
        SqlSessionFactory
        SqlSession

    SqlSessionFactoryBuilder --> SqlSessionFactory --> SqlSession


3. 从 XML 中构建 SqlSessionFactory
    通过官方的这句话,你能想到什么呢?
        第一:在MyBatis中一定是有一个很重要的对象,这个对象是:SqlSessionFactory对象。
        第二:SqlSessionFactory对象的创建需要XML。
    XML是什么?
        它一定是一个配置文件。

4. mybatis中有两个主要的配置文件:
    其中一个是:mybatis-config.xml,这是核心配置文件,主要配置连接数据库的信息等。(一个)
    另一个是:XxxxMapper.xml,这个文件是专门用来编写SQL语句的配置文件。(一个表一个)
        t_user表,一般会对应一个UserMapper.xml
        t_student表,一般会对应一个StudentMapper.xml

5. 关于第一个程序的小细节
    * mybatis中sql语句的结尾";"可以省略。
    * Resources.getResourceAsStream
        小技巧:以后凡是遇到resource这个单词,大部分情况下,这种加载资源的方式就是从类的根路径下开始加载。(开始查找)
        优点:采用这种方式,从类路径当中加载资源,项目的移植性很强。项目从windows移植到linux,代码不需要修改,因为这个资源文件一直都在类路径当中。
    * InputStream is = new FileInputStream("d:\\mybatis-config.xml");
        采用这种方式也可以。
        缺点:可移植性太差,程序不够健壮。可能会移植到其他的操作系统当中。导致以上路径无效,还需要修改java代码中的路径。这样违背了OCP原则。
    * 已经验证了:
        mybatis核心配置文件的名字,不一定是:mybatis-config.xml。可以是其它名字。
        mybatis核心配置文件存放的路径,也不一定是在类的根路径下。可以放到其它位置。但为了项目的移植性,健壮性,最好将这个配置文件放到类路径下面。
    * InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
        ClassLoader.getSystemClassLoader() 获取系统的类加载器。
        系统类加载器有一个方法叫做:getResourceAsStream
        它就是从类路径当中加载资源的。
        通过源代码分析发现:
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            底层的源代码其实就是:
            InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
    * CarMapper.xml文件的名字是固定的吗?CarMapper.xml文件的路径是固定的吗?
        都不是固定的。
        <mapper resource="CarMapper.xml"/> resource属性:这种方式是从类路径当中加载资源。
        <mapper url="file:///d:/CarMapper.xml"/> url属性:这种方式是从绝对路径当中加载资源。

6. 关于mybatis的事务管理机制。(深度剖析)

    * 在mybatis-config.xml文件中,可以通过以下的配置进行mybatis的事务管理
        <transactionManager type="JDBC"/>
    * type属性的值包括两个:
        JDBC(jdbc)
        MANAGED(managed)
        type后面的值,只有以上两个值可选,不区分大小写。
    * 在mybatis中提供了两种事务管理机制:
        第一种:JDBC事务管理器
        第二种:MANAGED事务管理器
    * JDBC事务管理器:
        mybatis框架自己管理事务,自己采用原生的JDBC代码去管理事务:
            conn.setAutoCommit(false); 开启事务。
            ....业务处理...
            conn.commit(); 手动提交事务
        使用JDBC事务管理器的话,底层创建的事务管理器对象:JdbcTransaction对象。

        如果你编写的代码是下面的代码:
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false);
            在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true。
            如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次。

    * MANAGED事务管理器:
        mybatis不再负责事务的管理了。事务管理交给其它容器来负责。例如:spring。
        我不管事务了,你来负责吧。

        对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED
        那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。

        没有人管理事务就是没有事务。

    * JDBC中的事务:
        如果你没有在JDBC代码中执行:conn.setAutoCommit(false);的话,默认的autoCommit是true。

    * 重点:
        以后注意了,只要你的autoCommit是true,就表示没有开启事务。
        只有你的autoCommit是false的时候,就表示开启了事务。

7. 关于mybatis集成日志组件。让我们调试起来更加方便。

    * mybatis常见的集成的日志组件有哪些呢?
        SLF4J(沙拉风):沙拉风是一个日志标准,其中有一个框架叫做logback,它实现了沙拉风规范。
        LOG4J
        LOG4J2
        STDOUT_LOGGING
        ....

        注意:log4j log4j2 logback都是同一个作者开发的。

    * 其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志。mybatis框架本身已经实现了这种标准。
    只要开启即可。怎么开启呢?在mybatis-config.xml文件中使用settings标签进行配置开启。
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
        这个标签在编写的时候要注意,它应该出现在environments标签之前。注意顺序。当然,不需要记忆这个顺序。
        因为有dtd文件进行约束呢。我们只要参考dtd约束即可。

        这种实现也是可以的,可以看到一些信息,比如:连接对象什么时候创建,什么时候关闭,sql语句是怎样的。
        但是没有详细的日期,线程名字,等。如果你想使用更加丰富的配置,可以集成第三方的log组件。

    * 集成logback日志框架。
        logback日志框架实现了slf4j标准。(沙拉风:日志门面。日志标准。)
        第一步:引入logback的依赖。
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.11</version>
            </dependency>
        第二步:引入logback所必须的xml配置文件。
            这个配置文件的名字必须叫做:logback.xml或者logback-test.xml,不能是其它的名字。
            这个配置文件必须放到类的根路径下。不能是其他位置。
            主要配置日志输出相关的级别以及日志具体的格式。

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"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbc"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

    </environments>
    <mappers>
        <!--sql映射文件创建好之后,需要将该文件路径配置到这里-->
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>

CarMapper.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 namespace="car">
    <!--insert sql:保存一个汽车信息-->
    <insert id="insertCar">
        insert into t_car
            (id,car_num,brand,guide_price,produce_time,car_type)
        values
            (null,'102','丰田mirai',40.30,'2014-10-05','氢能源')
    </insert>
</mapper>

mybatis.java

package aaa;

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.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class mybatis {
    public static void main(String[] args) throws IOException {

        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//        FileInputStream fileInputStream = new FileInputStream("mybatis-config.xml");
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");//默认根目录下
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int i = sqlSession.insert("insertCar");
        System.out.println(i);

        sqlSession.commit();

    }

}

完整实例程序

package aaa;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;

public class wanchengban {
    public static void main(String[] args) throws IOException {

        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(org.apache.ibatis.io.Resources.getResourceAsStream("mybatis-config.xml"));

        SqlSession sqlSession = sqlSessionFactory.openSession();
        int i = sqlSession.insert("insertCar");
        System.out.println(i);

        sqlSession.commit();
        sqlSession.rollback();
        sqlSession.close();


    }
}

引入JUnit


package com.powernode.mybatis;

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;

public class CarMapperTest {
    
    @Test
    public void testInsertCar(){
        SqlSession sqlSession = null;
        try {
            // 1.创建SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            // 2.创建SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
            // 3.创建SqlSession对象
            sqlSession = sqlSessionFactory.openSession();
            // 4.执行SQL
            int count = sqlSession.insert("insertCar");
            System.out.println("更新了几条记录:" + count);
            // 5.提交
            sqlSession.commit();
        } catch (Exception e) {
            // 回滚
            if (sqlSession != null) {
                sqlSession.rollback();
            }
            e.printStackTrace();
        } finally {
            // 6.关闭
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

引入日志框架logback

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>100MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

MyBatis工具类SqlSessionUtil的封装

package com.powernode.mybatis.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;

/**
 * MyBatis工具类
 * @version 1.0
 * @since 1.0
 */
public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;

    /**
     * 类加载时初始化sqlSessionFactory对象
     */
    static {
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
     *
     * @return 新的会话对象
     */
    public static SqlSession openSession() {
        return sqlSessionFactory.openSession(true);
    }
}

使用MyBatis完成CRUD

使用mybatis完成CRUD

1. 什么是CRUD
    C: Create增
    R: Retrieve查(检索)
    U: Update改
    D: Delete删

2. insert
    <insert id="insertCar">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values(null,'1003','丰田霸道',30.0,'2000-10-11','燃油车');
    </insert>
    这样写的问题是?
        值 显然是写死到配置文件中的。
        这个在实际开发中是不存在的。
        一定是前端的form表单提交过来数据。然后将值传给sql语句。

    例如:JDBC的代码是怎么写的?
        String sql = "insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)";
        ps.setString(1, xxx);
        ps.setString(2, yyy);
        ....

    在JDBC当中占位符采用的是?,在mybatis当中是什么呢?
        和?等效的写法是:#{}
        在mybatis当中不能使用?占位符,必须使用 #{} 来代替JDBC当中的 ?
        #{} 和 JDBC当中的 ? 是等效的。

    java程序中使用Map可以给SQL语句的占位符传值:
        Map<String, Object> map = new HashMap<>();
        map.put("k1", "1111");
        map.put("k2", "比亚迪汉");
        map.put("k3", 10.0);
        map.put("k4", "2020-11-11");
        map.put("k5", "电车");

        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{k1},#{k2},#{k3},#{k4},#{k5});
        注意:#{这里写什么?写map集合的key,如果key不存在,获取的是null}

        一般map集合的key起名的时候要见名知意。
            map.put("carNum", "1111");
            map.put("brand", "比亚迪汉2");
            map.put("guidePrice", 10.0);
            map.put("produceTime", "2020-11-11");
            map.put("carType", "电车");

            insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType});

    java程序中使用POJO类给SQL语句的占位符传值:
        Car car = new Car(null, "3333", "比亚迪秦", 30.0, "2020-11-11", "新能源");

        注意:占位符#{},大括号里面写:pojo类的属性名
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})

        把SQL语句写成这个德行:
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
                values(null,#{xyz},#{brand},#{guidePrice},#{produceTime},#{carType})
        出现了什么问题呢?
            There is no getter for property named 'xyz' in 'class com.powernode.mybatis.pojo.Car'
            mybatis去找:Car类中的getXyz()方法去了。没找到。报错了。

        怎么解决的?
            可以在Car类中提供一个getXyz()方法。这样问题就解决了。

        通过这个测试,得出一个结论:
            严格意义上来说:如果使用POJO对象传递值的话,#{}这个大括号中到底写什么?
                写的是get方法的方法名去掉get,然后将剩下的单词首字母小写,然后放进去。
                例如:getUsername() --> #{username}
                例如:getEmail() --> #{email}
                ....
        也就是说mybatis在底层给?传值的时候,先要获取值,怎么获取的?
            调用了pojo对象的get方法。例如:car.getCarNum(),car.getCarType(),car.getBrand()
3. delete
    * 需求:根据id删除数据
        将id=59的数据删除。

    实现:
        int count = sqlSession.delete("deleteById", 59);
        <delete id="deleteById">
            delete from t_car where id = #{fdsfd}
        </delete>
        注意:如果占位符只有一个,那么#{}的大括号里可以随意。但是最好见名知意。

4. update
    * 需求:根据id修改某条记录。

    实现:
        <update id="updateById">
            update t_car set
                 car_num=#{carNum},
                 brand=#{brand},
                 guide_price=#{guidePrice},
                 produce_time=#{produceTime},
                 car_type=#{carType}
            where
                id = #{id}
        </update>

        Car car = new Car(4L, "9999", "凯美瑞", 30.3, "1999-11-10", "燃油车");
        int count = sqlSession.update("updateById", car);

5. select(查一个,根据主键查询的话,返回的结果一定是一个。)
    * 需求:根据id查询。

    实现:
        <select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
            select * from t_car where id = #{id}
        </select>

        Object car = sqlSession.selectOne("selectById", 1);

    需要特别注意的是:
        select标签中resultType属性,这个属性用来告诉mybatis,查询结果集封装成什么类型的java对象。你需要告诉mybatis。
        resultType通常写的是:全限定类名。

    Car{id=1, carNum='null', brand='宝马520Li', guidePrice=null, produceTime='null', carType='null'}
    输出结果有点不对劲:
        id和brand属性有值。
        其他属性为null。

    carNum以及其他的这几个属性没有赋上值的原因是什么?
        select * from t_car where id = 1
        执行结果:
        +----+---------+-----------+-------------+--------------+----------+
        | id | car_num | brand     | guide_price | produce_time | car_type |
        +----+---------+-----------+-------------+--------------+----------+
        |  1 | 1001    | 宝马520Li |       10.00 | 2020-10-11   | 燃油车   |
        +----+---------+-----------+-------------+--------------+----------+
        car_num、guide_price、produce_time、car_type这是查询结果的列名。
        这些列名和Car类中的属性名对不上。
        Car类的属性名:
        carNum、guidePrice、produceTime、carType

        那这个问题怎么解决呢?
            select语句查询的时候,查询结果集的列名是可以使用as关键字起别名的。

        <select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
            select
                id,car_num as carNum,brand,guide_price as guidePrice,
                produce_time as produceTime,
                car_type as carType
            from
                t_car
            where
                id = #{id}
        </select>
        起别名之后:
        +----+--------+-----------+------------+-------------+---------+
        | id | carNum | brand     | guidePrice | produceTime | carType |
        +----+--------+-----------+------------+-------------+---------+
        |  1 | 1001   | 宝马520Li |      10.00 | 2020-10-11  | 燃油车  |
        +----+--------+-----------+------------+-------------+---------+



6. select(查所有的)

    <select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
        select
            id,car_num as carNum,brand,guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from
            t_car
    </select>

    List<Object> cars = sqlSession.selectList("selectAll");

    注意:resultType还是指定要封装的结果集的类型。不是指定List类型,是指定List集合中元素的类型。
    selectList方法:mybatis通过这个方法就可以得知你需要一个List集合。它会自动给你返回一个List集合。

7. 在sql mapper.xml文件当中有一个namespace,这个属性是用来指定命名空间的。用来防止id重复。
怎么用?
    在xml文件中:
        <mapper namespace="aaaaaaaaa">
            <select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
                select
                    id,car_num as carNum,brand,guide_price as guidePrice,
                    produce_time as produceTime,
                    car_type
                from
                    t_car
            </select>
        </mapper>
    在java程序中的写法:
        List<Object> cars = sqlSession.selectList("aaaaaaaaa.selectAll");

    实际上,本质上,mybatis中的sqlId的完整写法:
        namespace.id

insert(Create)

package com.powernode.mybatis;

import com.powernode.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

/**
 * 测试MyBatis的CRUD
 * @version 1.0
 * @since 1.0
 */
public class CarMapperTest {
    @Test
    public void testInsertCar(){
        // 准备数据
        Map<String, Object> map = new HashMap<>();
        map.put("k1", "103");
        map.put("k2", "奔驰E300L");
        map.put("k3", 50.3);
        map.put("k4", "2020-10-01");
        map.put("k5", "燃油车");
        // 获取SqlSession对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        // 执行SQL语句(使用map集合给sql语句传递数据)
        int count = sqlSession.insert("insertCar", map);
        System.out.println("插入了几条记录:" + count);
    }
}
<?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 namespace="car">
    <insert id="insertCar">
        insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{k1},#{k2},#{k3},#{k4},#{k5})
    </insert>
</mapper>

package com.powernode.mybatis.pojo;

/**
 * POJOs,简单普通的Java对象。封装数据用的。
 * @version 1.0
 * @since 1.0
 */
public class Car {
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", carNum='" + carNum + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", carType='" + carType + '\'' +
                '}';
    }

    public Car() {
    }

    public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carNum = carNum;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCarNum() {
        return carNum;
    }

    public void setCarNum(String carNum) {
        this.carNum = carNum;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getGuidePrice() {
        return guidePrice;
    }

    public void setGuidePrice(Double guidePrice) {
        this.guidePrice = guidePrice;
    }

    public String getProduceTime() {
        return produceTime;
    }

    public void setProduceTime(String produceTime) {
        this.produceTime = produceTime;
    }

    public String getCarType() {
        return carType;
    }

    public void setCarType(String carType) {
        this.carType = carType;
    }
}

delete(Delete)

update(Update)

    @Test
    public void testUpdateCarByPOJO(){
        // 准备数据
        Car car = new Car();
        car.setId(34L);
        car.setCarNum("102");
        car.setBrand("比亚迪汉");
        car.setGuidePrice(30.23);
        car.setProduceTime("2018-09-10");
        car.setCarType("电车");
        // 获取SqlSession对象
        SqlSession sqlSession = SqlSessionUtil.openSession();
        // 执行SQL语句
        int count = sqlSession.update("updateCarByPOJO", car);
        System.out.println("更新了几条记录:" + count);
    }
    @Test
    public void test5() throws IOException {
        car car = new car();
        car.setId(19L);
        car.setCarNum("123456");
        car.setBrand("宝马");
        car.setGuidePrice(100000);
        car.setProduceTime("2020-01-01");
        car.setCarType("SUV");

        SqlSession sqlSession = fz.openSession();
        int update = sqlSession.update("update", car);
        System.out.println(update+"条记录");
        sqlSession.commit();
        sqlSession.close();
    }
package aaa;

public class car {
    private Long id;
    private String carNum;
    private String brand;
    private double guidePrice;
    private String produceTime;
    private String carType;


    public car() {
    }

    public car(Long id, String carNum, String brand, double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carNum = carNum;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    /**
     * 获取
     * @return id
     */
    public Long getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * 获取
     * @return carNum
     */
    public String getCarNum() {
        return carNum;
    }

    /**
     * 设置
     * @param carNum
     */
    public void setCarNum(String carNum) {
        this.carNum = carNum;
    }

    /**
     * 获取
     * @return brand
     */
    public String getBrand() {
        return brand;
    }

    /**
     * 设置
     * @param brand
     */
    public void setBrand(String brand) {
        this.brand = brand;
    }

    /**
     * 获取
     * @return guidePrice
     */
    public double getGuidePrice() {
        return guidePrice;
    }

    /**
     * 设置
     * @param guidePrice
     */
    public void setGuidePrice(double guidePrice) {
        this.guidePrice = guidePrice;
    }

    /**
     * 获取
     * @return produceTime
     */
    public String getProduceTime() {
        return produceTime;
    }

    /**
     * 设置
     * @param produceTime
     */
    public void setProduceTime(String produceTime) {
        this.produceTime = produceTime;
    }

    /**
     * 获取
     * @return carType
     */
    public String getCarType() {
        return carType;
    }

    /**
     * 设置
     * @param carType
     */
    public void setCarType(String carType) {
        this.carType = carType;
    }

    public String toString() {
        return "car{id = " + id + ", carNum = " + carNum + ", brand = " + brand + ", guidePrice = " + guidePrice + ", produceTime = " + produceTime + ", carType = " + carType + "}";
    }
}

select(Retrieve)

查询一条数据

查询多条数据

关于xxxMapper的namespace

MyBatis核心配置文件详解

<?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"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="CarMapper.xml"/>
        <mapper resource="CarMapper2.xml"/>
    </mappers>
</configuration>

 @Test
    public void test9() throws IOException {
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "developmentt");
        SqlSession sqlSession = sqlSessionFactory.openSession();
//        Map<String,Object>map=new HashMap<>();
//        map.put("k1","11111111");
//        map.put("k2","22222222");
//        map.put("k3","33333333");
//        map.put("k4","4444");
//        map.put("k5","5555");
        car car = new car();
//        car.setId(99L);
        car.setCarNum("123456789");
        car.setBrand("宝马ma");

        car.setGuidePrice(100000);
        car.setProduceTime("2020-01-01");
        car.setCarType("SUVVVV");

        sqlSession.insert("insertCar1", car);

        sqlSession.commit();
        sqlSession.close();

    }

<?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>

    <!--java.util.Properties类。是一个Map集合。key和value都是String类型-->
    <!--在properties标签中可以配置很多属性-->
    <!--<properties>-->
        <!--这是其中的一个属性-->
        <!--<property name="属性名" value="属性值"/>-->
        <!--<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/powernode"/>
        <property name="jdbc.username" value="root"/>
        <property name="jdbc.password" value="root"/>-->
    <!--</properties>-->

    <!--resource,一定是从类路径下开始查找资源-->
    <properties resource="jdbc.properties" />

    <!--从绝对路径当中加载资源。绝对路径怎么写?file:///路径-->
    <!--<properties url="file:///d:/jdbc.properties" />-->



    <!--default表示默认使用的环境。-->
    <!--默认环境什么意思?当你使用mybatis创建SqlSessionFactory对象的时候,没有指定环境的话,默认使用哪个环境。-->
    <environments default="powernodeDB">

        <!--其中的一个环境。连接的数据库是powernode-->
        <!--一般一个数据库会对应一个SqlSessionFactory对象。-->
        <!--一个环境environment会对应一个SqlSessionFactory对象-->
        <environment id="powernodeDB">
            <!--
                transactionManager标签:
                    1.作用:配置事务管理器。指定mybatis具体使用什么方式去管理事务。
                    2.type属性有两个值:
                        第一个:JDBC: 使用原生的JDBC代码来管理事务。
                            conn.setAutoCommit(false);
                            ....
                            conn.commit();
                        第二个:MANAGED:mybatis不再负责事务的管理,将事务管理交给其它的JEE(JavaEE)容器来管理。例如:spring
                    3. 大小写无所谓。不缺分大小写。但是不能写其他值。只能是二选一:
                        jdbc、managed
                    4. 在mybatis中提供了一个事务管理器接口:Transaction
                        该接口下有两个实现类:
                            JdbcTransaction
                            ManagedTransaction
                        如果type="JDBC",那么底层会实例化JdbcTransaction对象。
                        如果type="MANAGED",那么底层会实例化ManagedTransaction
            -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource配置:
                    1.dataSource被称为数据源。
                    2.dataSource作用是什么?为程序提供Connection对象。(但凡是给程序提供Connection对象的,都叫做数据源。)
                    3.数据源实际上是一套规范。JDK中有这套规范:javax.sql.DataSource(这个数据源的规范,这套接口实际上是JDK规定的。)
                    4.我们自己也可以编写数据源组件,只要实现javax.sql.DataSource接口就行了。实现接口当中所有的方法。这样就有了自己的数据源。
                    比如你可以写一个属于自己的数据库连接池(数据库连接池是提供连接对象的,所以数据库连接池就是一个数据源)。
                    5.常见的数据源组件有哪些呢【常见的数据库连接池有哪些呢】?
                        阿里巴巴的德鲁伊连接池:druid
                        c3p0
                        dbcp
                        ....
                    6. type属性用来指定数据源的类型,就是指定具体使用什么方式来获取Connection对象:
                        type属性有三个值:必须是三选一。
                        type="[UNPOOLED|POOLED|JNDI]"
                        UNPOOLED:不使用数据库连接池技术。每一次请求过来之后,都是创建新的Connection对象。
                        POOLED:使用mybatis自己实现的数据库连接池。
                        JNDI:集成其它第三方的数据库连接池。

                        JNDI是一套规范。谁实现了这套规范呢?大部分的web容器都实现了JNDI规范:
                            例如:Tomcat、Jetty、WebLogic、WebSphere,这些服务器(容器)都实现了JNDI规范。
                        JNDI是:java命名目录接口。Tomcat服务器实现了这个规范。
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
                <!--提醒:正常使用连接池的话,池中有很多参数是需要设置的。设置好参数,可以让连接池发挥的更好。事半功倍的效果。-->
                <!--具体连接池当中的参数如何配置呢?需要反复的根据当前业务情况进行测试。-->
                <!--poolMaximumActiveConnections:连接池当中最多的正在使用的连接对象的数量上限。最多有多少个连接可以活动。默认值10-->
                <property name="poolMaximumActiveConnections" value="10"/>
                <!--每隔2秒打印日志,并且尝试获取连接对象-->
                <property name="poolTimeToWait" value="2000"/>
                <!--强行让某个连接空闲,超时时间的设置-->
                <property name="poolMaximumCheckoutTime" value="10000"/>
                <!--最多的空闲数量-->
                <property name="poolMaximumIdleConnections" value="5"/>
            </dataSource>

        </environment>

        <!--这是mybatis的另一个环境,也就是连接的数据库是另一个数据库mybatis-->
        <environment id="mybatisDB">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>

    </environments>

    <mappers>
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>

核心配置文件各个部分代码解释

environment

transactionManager

dataSource

properties

mapper

手写MyBatis框架(掌握原理)

dom4j解析XML文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.group</groupId>
    <artifactId>parse-xml-by-dom4j</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <!--dom4j依赖-->
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--jaxen依赖-->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.2.0</version>
        </dependency>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

</project>

GodBatis

在WEB中应用MyBatis(使用MVC架构模式)

需求描述

数据库表的设计和准备数据

实现步骤(从前往后写)

建数据库表

添加所需的依赖pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.at</groupId>
  <artifactId>bank2</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>bank2 Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.13</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.5.6</version>
    </dependency>

  </dependencies>
  <build>
    <finalName>bank2</finalName>
  </build>
</project>

配置tomcat

准备页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/bank2/transfer" method="post">
  转出账号:<input type="text" name="fromActno">
    <br>
  转入账号:<input type="text" name="toActno">
    <br>
  转账金额:<input type="text" name="money">
    <br>
  <input type="submit" value="转账">

</form>

</body>
</html>

准备MyBatis等配置文件

分层次MVC

pojo

package AAAA.pojo;
//封装账户数据  根据数据库里面的
public class Account {

    private Long id;
    private String actno;
    private Double balance;


    public Account() {
    }

    public Account(Long id, String actno, Double balance) {
        this.id = id;
        this.actno = actno;
        this.balance = balance;
    }

    /**
     * 获取
     * @return id
     */
    public Long getId() {
        return id;
    }

    /**
     * 设置
     * @param id
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * 获取
     * @return actno
     */
    public String getActno() {
        return actno;
    }

    /**
     * 设置
     * @param actno
     */
    public void setActno(String actno) {
        this.actno = actno;
    }

    /**
     * 获取
     * @return balance
     */
    public Double getBalance() {
        return balance;
    }

    /**
     * 设置
     * @param balance
     */
    public void setBalance(Double balance) {
        this.balance = balance;
    }

    public String toString() {
        return "Account{id = " + id + ", actno = " + actno + ", balance = " + balance + "}";
    }
}

utils

package AAAA.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;

public class fz {

    private  fz(){};
    private static SqlSessionFactory sqlSessionFactory;

    /**
     * 类加载时初始化sqlSessionFactory对象
     */
    static {
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
    }



    public static SqlSession openSession() {
        return sqlSessionFactory.openSession();
    }
}

web

service

dao

AccountMapper.xml

exception

package AAAA.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;

public class fz {

    private  fz(){};
    private static SqlSessionFactory sqlSessionFactory;

    /**
     * 类加载时初始化sqlSessionFactory对象
     */
    static {
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
    }
    
    private static ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
    
    public static SqlSession openSession() {
        SqlSession sqlSession = local.get();
        if(sqlSession != null){
            sqlSession=sqlSessionFactory.openSession();
            //将sqlsession对象绑定到当前线程
            local.set(sqlSession);
        }
        return sqlSession;
        
    }
    public static void closeSession(SqlSession sqlSession){
        if(sqlSession != null){
            sqlSession.close();
            local.remove();
        }
        
    }
}

MyBatis对象作用域

事务问题

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.dao.impl.AccountDaoImpl;
import com.powernode.bank.exception.AppException;
import com.powernode.bank.exception.MoneyNotEnoughException;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
        // 查询转出账户的余额
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
            throw new MoneyNotEnoughException("对不起,您的余额不足。");
        }
        try {
            // 程序如果执行到这里说明余额充足
            // 修改账户余额
            Account toAct = accountDao.selectByActno(toActno);
            fromAct.setBalance(fromAct.getBalance() - money);
            toAct.setBalance(toAct.getBalance() + money);
            // 更新数据库(添加事务)
            SqlSession sqlSession = SqlSessionUtil.openSession();
            accountDao.update(fromAct);
            // 模拟异常
            String s = null;
            s.toString();
            accountDao.update(toAct);
            sqlSession.commit();
            sqlSession.close();
        } catch (Exception e) {
            throw new AppException("转账失败,未知原因!");
        }
    }
}

package com.powernode.bank.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;

/**
 * MyBatis工具类
 *
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;

    /**
     * 类加载时初始化sqlSessionFactory对象
     */
    static {
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    /**
     * 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
     *
     * @return 新的会话对象
     */
    public static SqlSession openSession() {
        SqlSession sqlSession = local.get();
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 关闭SqlSession对象
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
        }
        local.remove();
    }
}

package com.powernode.bank.service.impl;

import com.powernode.bank.dao.AccountDao;
import com.powernode.bank.dao.impl.AccountDaoImpl;
import com.powernode.bank.exception.AppException;
import com.powernode.bank.exception.MoneyNotEnoughException;
import com.powernode.bank.pojo.Account;
import com.powernode.bank.service.AccountService;
import com.powernode.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao = new AccountDaoImpl();

    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
        // 查询转出账户的余额
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
            throw new MoneyNotEnoughException("对不起,您的余额不足。");
        }
        try {
            // 程序如果执行到这里说明余额充足
            // 修改账户余额
            Account toAct = accountDao.selectByActno(toActno);
            fromAct.setBalance(fromAct.getBalance() - money);
            toAct.setBalance(toAct.getBalance() + money);
            // 更新数据库(添加事务)
            SqlSession sqlSession = SqlSessionUtil.openSession();
            accountDao.update(fromAct);
            // 模拟异常
            String s = null;
            s.toString();
            accountDao.update(toAct);
            sqlSession.commit();
            SqlSessionUtil.close(sqlSession);  // 只修改了这一行代码。
        } catch (Exception e) {
            throw new AppException("转账失败,未知原因!");
        }
    }
}

分析当前程序存在的问题

使用javassist生成类

使用Javassist生成DaoImpl类

package com.powernode.bank.utils;

import org.apache.ibatis.javassist.CannotCompileException;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtClass;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * 使用javassist库动态生成dao接口的实现类
 *
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class GenerateDaoByJavassist {

    /**
     * 根据dao接口生成dao接口的代理对象
     *
     * @param sqlSession   sql会话
     * @param daoInterface dao接口
     * @return dao接口代理对象
     */
    public static Object getMapper(SqlSession sqlSession, Class daoInterface) {
        ClassPool pool = ClassPool.getDefault();
        // 生成代理类
        CtClass ctClass = pool.makeClass(daoInterface.getPackageName() + ".impl." + daoInterface.getSimpleName() + "Impl");
        // 接口
        CtClass ctInterface = pool.makeClass(daoInterface.getName());
        // 代理类实现接口
        ctClass.addInterface(ctInterface);
        // 获取所有的方法
        Method[] methods = daoInterface.getDeclaredMethods();
        Arrays.stream(methods).forEach(method -> {
            // 拼接方法的签名
            StringBuilder methodStr = new StringBuilder();
            String returnTypeName = method.getReturnType().getName();
            methodStr.append(returnTypeName);
            methodStr.append(" ");
            String methodName = method.getName();
            methodStr.append(methodName);
            methodStr.append("(");
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                methodStr.append(parameterTypes[i].getName());
                methodStr.append(" arg");
                methodStr.append(i);
                if (i != parameterTypes.length - 1) {
                    methodStr.append(",");
                }
            }
            methodStr.append("){");
            // 方法体当中的代码怎么写?
            // 获取sqlId(这里非常重要:因为这行代码导致以后namespace必须是接口的全限定接口名,sqlId必须是接口中方法的方法名。)
            String sqlId = daoInterface.getName() + "." + methodName;
            // 获取SqlCommondType
            String sqlCommondTypeName = sqlSession.getConfiguration().getMappedStatement(sqlId).getSqlCommandType().name();
            if ("SELECT".equals(sqlCommondTypeName)) {
                methodStr.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();");
                methodStr.append("Object obj = sqlSession.selectOne(\"" + sqlId + "\", arg0);");
                methodStr.append("return (" + returnTypeName + ")obj;");
            } else if ("UPDATE".equals(sqlCommondTypeName)) {
                methodStr.append("org.apache.ibatis.session.SqlSession sqlSession = com.powernode.bank.utils.SqlSessionUtil.openSession();");
                methodStr.append("int count = sqlSession.update(\"" + sqlId + "\", arg0);");
                methodStr.append("return count;");
            }
            methodStr.append("}");
            System.out.println(methodStr);
            try {
                // 创建CtMethod对象
                CtMethod ctMethod = CtMethod.make(methodStr.toString(), ctClass);
                ctMethod.setModifiers(Modifier.PUBLIC);
                // 将方法添加到类
                ctClass.addMethod(ctMethod);
            } catch (CannotCompileException e) {
                throw new RuntimeException(e);
            }
        });
        try {
            // 创建代理对象
            Class<?> aClass = ctClass.toClass();
            Constructor<?> defaultCon = aClass.getDeclaredConstructor();
            Object o = defaultCon.newInstance();
            return o;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

MyBatis中接口代理机制及使用

MyBatis小技巧

#{}和${}

package com.powernode.mybatis.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;

/**
 * MyBatis工具类
 *
 * @author 老杜
 * @version 1.0
 * @since 1.0
 */
public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;

    /**
     * 类加载时初始化sqlSessionFactory对象
     */
    static {
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    /**
     * 每调用一次openSession()可获取一个新的会话,该会话支持自动提交。
     *
     * @return 新的会话对象
     */
    public static SqlSession openSession() {
        SqlSession sqlSession = local.get();
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 关闭SqlSession对象
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
        }
        local.remove();
    }
}

拼接表名

批量删除

模糊查询

使用${}

使用#{}

别名机制 typeAliases

mappers

总结

mybatis小技巧
1. #{}和${}的区别

#{}的执行结果:
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==>  Preparing: select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where car_type = ?
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==> Parameters: 新能源(String)
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - <==      Total: 2

${}的执行结果:
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==>  Preparing: select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where car_type = 新能源
[main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==> Parameters:

org.apache.ibatis.exceptions.PersistenceException:
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column '新能源' in 'where clause'
### The error may exist in CarMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select             id,             car_num as carNum,             brand,             guide_price as guidePrice,             produce_time as produceTime,             car_type as carType         from             t_car         where             car_type = 新能源
### Cause: java.sql.SQLSyntaxErrorException: Unknown column '新能源' in 'where clause'

#{}和${}的区别:
    #{}: 底层使用PreparedStatement。特点:先进行SQL语句的编译,然后给SQL语句的占位符问号?传值。可以避免SQL注入的风险。
    ${}:底层使用Statement。特点:先进行SQL语句的拼接,然后再对SQL语句进行编译。存在SQL注入的风险。
    优先使用#{},这是原则。避免SQL注入的风险。

#{}的执行结果:
Preparing: select
                id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
           from t_car order by produce_time ?
Parameters: asc(String)

select
    id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
from t_car order by produce_time 'asc'

${}的执行结果:
Preparing:
    select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
    from t_car order by produce_time asc
Parameters:

如果需要SQL语句的关键字放到SQL语句中,只能使用${},因为#{}是以值的形式放到SQL语句当中的。

2. 向SQL语句当中拼接表名,就需要使用${}
    现实业务当中,可能会存在分表存储数据的情况。因为一张表存的话,数据量太大。查询效率比较低。
    可以将这些数据有规律的分表存储,这样在查询的时候效率就比较高。因为扫描的数据量变少了。
    日志表:专门存储日志信息的。如果t_log只有一张表,这张表中每一天都会产生很多log,慢慢的,这个表中数据会很多。
    怎么解决问题?
        可以每天生成一个新表。每张表以当天日期作为名称,例如:
            t_log_20220901
            t_log_20220902
            ....
    你想知道某一天的日志信息怎么办?
        假设今天是20220901,那么直接查:t_log_20220901的表即可。

3.批量删除:一次删除多条记录。
    批量删除的SQL语句有两种写法:
        第一种or:delete from t_car where id=1 or id=2 or id=3;
        第二种int:delete from t_car where id in(1,2,3);

    应该采用${}的方式:
        delete from t_car where id in(${ids});

4.模糊查询:like
    需求:根据汽车品牌进行模糊查询
        select * from t_car where brand like '%奔驰%';
        select * from t_car where brand like '%比亚迪%';

    第一种方案:
        '%${brand}%'
    第二种方案:concat函数,这个是mysql数据库当中的一个函数,专门进行字符串拼接
        concat('%',#{brand},'%')
    第三种方案:比较鸡肋了。可以不算。
        concat('%','${brand}','%')
    第四种方案:
        "%"#{brand}"%"

5. 关于MyBatis中别名机制:
    <typeAliases>
        <!--别名自己指定的-->
        <typeAlias type="com.powernode.mybatis.pojo.Car" alias="aaa"/>
        <typeAlias type="com.powernode.mybatis.pojo.Log" alias="bbb"/>

        <!--采用默认的别名机制-->
        <typeAlias type="com.powernode.mybatis.pojo.Car"/>
        <typeAlias type="com.powernode.mybatis.pojo.Log"/>

        <!--包下所有的类自动起别名。使用简名作为别名。-->
        <package name="com.powernode.mybatis.pojo"/>
    </typeAliases>

    所有别名不区分大小写。
    namespace不能使用别名机制。
6. mybatis-config.xml文件中的mappers标签。
    <mapper resource="CarMapper.xml"/> 要求类的根路径下必须有:CarMapper.xml
    <mapper url="file:///d:/CarMapper.xml"/> 要求在d:/下有CarMapper.xml文件
    <mapper class="全限定接口名,带有包名"/>

    mapper标签的属性可以有三个:
        resource:这种方式是从类的根路径下开始查找资源。采用这种方式的话,你的配置文件需要放到类路径当中才行。
        url: 这种方式是一种绝对路径的方式,这种方式不要求配置文件必须放到类路径当中,哪里都行,只要提供一个绝对路径就行。这种方式使用极少,因为移植性太差。
        class: 这个位置提供的是mapper接口的全限定接口名,必须带有包名的。
            思考:mapper标签的作用是指定SqlMapper.xml文件的路径,指定接口名有什么用呢?
                <mapper class="com.powernode.mybatis.mapper.CarMapper"/>
                如果你class指定是:com.powernode.mybatis.mapper.CarMapper
                那么mybatis框架会自动去com/powernode/mybatis/mapper目录下查找CarMapper.xml文件。
            注意:也就是说:如果你采用这种方式,那么你必须保证CarMapper.xml文件和CarMapper接口必须在同一个目录下。并且名字一致。
            CarMapper接口-> CarMapper.xml
            LogMapper接口-> LogMapper.xml
            ....

    提醒!!!!!!!!!!!!!!!!!!!!!!!
        在IDEA的resources目录下新建多重目录的话,必须是这样创建:
            com/powernode/mybatis/mapper
        不能这样:
            com.powernode.mybatis.mapper




idea配置文件模板

插入数据时获取自动生成的主键

MyBatis参数处理

单个简单类型参数

Mapper.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">

<mapper namespace="com.powernode.mybatis.mapper.StudentMapper">

    <select id="selectByNameAndSex2" resultType="Student">
        <!--使用了@Param注解之后,arg0和arg1失效了-->
        <!--select * from t_student where name = #{arg0} and sex = #{arg1}-->

        <!--使用了@Param注解之后,param1和param2还可以用-->
        <!--select * from t_student where name = #{param1} and sex = #{param2}-->

        select * from t_student where name = #{name} and sex = #{sex}

    </select>

    <!--
        注意:低版本的mybatis中,使用的是:#{0}和#{1},以及#{2}...
        高版本的mybatis中,使用的是:
            #{arg0}
            #{arg1}
            #{arg2}
            #{arg3}
            #{arg4}

            #{param1}
            #{param2}
            #{param3}
            #{param4}
    -->
    <select id="selectByNameAndSex" resultType="Student">
        <!--select * from t_student where name = #{arg0} and sex = #{arg1}-->
        <!--select * from t_student where name = #{param1} and sex = #{param2}-->
        select * from t_student where name = #{arg0} and sex = #{param2}
    </select>

    <!--<insert id="insertStudentByPOJO" parameterType="student">-->
    <insert id="insertStudentByPOJO">
        insert into t_student(id,name,age,sex,birth,height) values(null,#{name},#{age},#{sex},#{birth},#{height})
    </insert>

    <!--<insert id="insertStudentByMap" parameterType="map">-->
    <insert id="insertStudentByMap">
        insert into t_student(id,name,age,sex,birth,height) values(null,#{姓名},#{年龄},#{性别},#{生日},#{身高})
    </insert>

    <!--
    List<Student> selectById(Long id);
    List<Student> selectByName(String name);
    List<Student> selectByBirth(Date birth);
    List<Student> selectBySex(Character sex);

    parameterType属性的作用:
        告诉mybatis框架,我这个方法的参数类型是什么类型。
        mybatis框架自身带有类型自动推断机制,所以大部分情况下parameterType属性都是可以省略不写的。

        SQL语句最终是这样的:
            select * from t_student where id = ?
        JDBC代码是一定要给?传值的。
        怎么传值?ps.setXxx(第几个问号, 传什么值);
            ps.setLong(1, 1L);
            ps.setString(1, "zhangsan");
            ps.setDate(1, new Date());
            ps.setInt(1, 100);
            ...
        mybatis底层到底调用setXxx的哪个方法,取决于parameterType属性的值。

    注意:mybatis框架实际上内置了很多别名。可以参考开发手册。
    -->
    <select id="selectById" resultType="Student" parameterType="long">
        select * from t_student where id = #{id}
    </select>

    <select id="selectByName" resultType="student">
        select * from t_student where name = #{name, javaType=String, jdbcType=VARCHAR}
    </select>

    <select id="selectByBirth" resultType="student">
        select * from t_student where birth = #{birth}
    </select>

    <select id="selectBySex" resultType="student">
        select * from t_student where sex = #{sex}
    </select>

</mapper>
import AAA.mapper.StudentMapper;
import AAA.pojo.Student;
import AAA.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.testng.annotations.Test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class ttttttest {

    @Test
    public void testSelectById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectById(2L);
        for (Student student : students) {
            System.out.println(student);
        }
        sqlSession.close();

    }

    @Test
    public void testSelectByName(){
        StudentMapper mapper = SqlSessionUtil.openSession().getMapper(StudentMapper.class);
        List<Student> students = mapper.selectByName("张三");
        students.forEach(student-> System.out.println(student));
        SqlSessionUtil.openSession().close();
    }

    @Test
    public void testSelectBirth() throws ParseException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date parse = format.parse("2024-08-06");

        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectByBirth(parse);
        students.forEach(student-> System.out.println(student));
    }

    @Test
    public void testSelectBySex(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//        List<Student> students = mapper.selectBySex('女');
        Character nan = Character.valueOf('男');
        List<Student> students = mapper.selectBySex(nan);
        students.forEach(student-> System.out.println(student));
        sqlSession.close();
    }

}
<?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">


<mapper namespace="AAA.mapper.StudentMapper">


    <select id="selectById" resultType="Student" parameterType="java.lang.Long">
        select *from t_student where id = #{id};

    </select>
    <select id="selectByName" resultType="AAA.pojo.Student">
    select *from t_student where name = #{name,javaType=String,jdbcType=VARCHAR};

    </select>
    <select id="selectByBirth" resultType="AAA.pojo.Student">

    select *from t_student where birth = #{birth,javaType=java.util.Date,jdbcType=DATE};
    </select>
    <select id="selectBySex" resultType="AAA.pojo.Student">

    select *from t_student where sex = #{sex,javaType=java.lang.Character,jdbcType=CHAR};
    </select>
</mapper>
package AAA.mapper;

import AAA.pojo.Student;

import java.util.Date;
import java.util.List;

public interface StudentMapper {

    //当接口的方法的参数只有一个单个参数,并且参数的数据类型都是简单类型
    //根据id name brith sex查询

    List<Student> selectById(Long id);
    List<Student> selectByName(String name);
    List<Student> selectByBirth(Date birth);
    List<Student> selectBySex(Character sex);
}

Map参数

 @Test
    public void testInsertByMap(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Map<String, Object> map = new HashMap<>();

        map.put("name","lisi");
        map.put("sex","男");
        map.put("birth",new Date());
        map.put("height",1.7);
        map.put("age",20);


        mapper.insertStudentByMap(map);

        sqlSession.commit();
        sqlSession.close();


    }

实体类参数(POJO类)

 @Test
    public void testInsertByPOJO(){

        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//        Student student = new Student(null,"张三",200,1.7,new Date(),"男");
        Student student = new Student();
        student.setAge(2000);
        student.setBirth(new Date());
        student.setHeight(1.77777);
        student.setName("张三san");
        student.setSex("男");
        mapper.insertStudentByPOJO(student);
        sqlSession.commit();
        sqlSession.close();
    }

多参数

@Param注解(命名参数)

@Param源码分析

MyBatis查询语句专题

返回Car(POJO类)

返回List<Car>

返回Map

返回List<Map>

返回Map<String,Map>

resultMap结果映射

1.as 给列起别名

2.使用resultMap进行结果映射

3.是否开启驼峰命名自动映射

返回总记录条数

动态SQL

if标签

where标签

trim标签

set标签

choose when otherwise

foreach标签

批量删除

批量添加

sql标签与include标签

MyBatis的高级映射及延迟加载

多对一

第一种方式:级联属性映射

第二种方式:association

第三种方式:分步查询

多对一延迟加载

一对多

第一种方式:collection

第二种方式:分步查询

一对多延迟加载

MyBatis的缓存

一级缓存

package com.powernode.mybatis.test;

import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
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;

public class CarMapperTest {

    @Test
    public void testSelectById() throws Exception{
        // 注意:不能使用我们封装的SqlSessionUtil工具类。
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));

        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
        Car car1 = mapper1.selectById(83L);
        System.out.println(car1);

        CarMapper mapper2 = sqlSession1.getMapper(CarMapper.class);
        Car car2 = mapper2.selectById(83L);
        System.out.println(car2);

        SqlSession sqlSession2 = sqlSessionFactory.openSession();

        CarMapper mapper3 = sqlSession2.getMapper(CarMapper.class);
        Car car3 = mapper3.selectById(83L);
        System.out.println(car3);

        CarMapper mapper4 = sqlSession2.getMapper(CarMapper.class);
        Car car4 = mapper4.selectById(83L);
        System.out.println(car4);

    }
}

二级缓存

MyBatis集成EhCache

MyBatis的逆向工程

逆向工程配置与生成

<!--定制构建过程-->
<build>
  <!--可配置多个插件-->
  <plugins>
    <!--其中的一个插件:mybatis逆向工程插件-->
    <plugin>
      <!--插件的GAV坐标-->
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-maven-plugin</artifactId>
      <version>1.4.1</version>
      <!--允许覆盖-->
      <configuration>
        <overwrite>true</overwrite>
      </configuration>
      <!--插件的依赖-->
      <dependencies>
        <!--mysql驱动依赖-->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.30</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--
        targetRuntime有两个值:
            MyBatis3Simple:生成的是基础版,只有基本的增删改查。
            MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!--防止生成重复代码-->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
      
        <commentGenerator>
            <!--是否去掉生成日期-->
            <property name="suppressDate" value="true"/>
            <!--是否去除注释-->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--连接数据库信息-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/powernode"
                        userId="root"
                        password="root">
        </jdbcConnection>

        <!-- 生成pojo包名和位置 -->
        <javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java">
            <!--是否开启子包-->
            <property name="enableSubPackages" value="true"/>
            <!--是否去除字段名的前后空白-->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- 生成SQL映射文件的包名和位置 -->
        <sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources">
            <!--是否开启子包-->
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- 生成Mapper接口的包名和位置 -->
        <javaClientGenerator
                type="xmlMapper"
                targetPackage="com.powernode.mybatis.mapper"
                targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 表名和对应的实体类名-->
        <table tableName="t_car" domainObjectName="Car"/>

    </context>
</generatorConfiguration>

测试逆向工程生成

package com.powernode.mybatis.test;

import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.pojo.CarExample;
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.math.BigDecimal;
import java.util.List;

public class GeneratorTest {
    @Test
    public void testGenerator() throws Exception{
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        // 增
        /*Car car = new Car();
        car.setCarNum("1111");
        car.setBrand("比亚迪唐");
        car.setGuidePrice(new BigDecimal(30.0));
        car.setProduceTime("2010-10-12");
        car.setCarType("燃油车");
        int count = mapper.insert(car);
        System.out.println("插入了几条记录:" + count);*/
        // 删
        /*int count = mapper.deleteByPrimaryKey(83L);
        System.out.println("删除了几条记录:" + count);*/
        // 改
        // 根据主键修改
        /*Car car = new Car();
        car.setId(89L);
        car.setGuidePrice(new BigDecimal(20.0));
        car.setCarType("新能源");
        int count = mapper.updateByPrimaryKey(car);
        System.out.println("更新了几条记录:" + count);*/
        // 根据主键选择性修改
        /*car = new Car();
        car.setId(89L);
        car.setCarNum("3333");
        car.setBrand("宝马520Li");
        car.setProduceTime("1999-01-10");
        count = mapper.updateByPrimaryKeySelective(car);
        System.out.println("更新了几条记录:" + count);*/

        // 查一个
        Car car = mapper.selectByPrimaryKey(89L);
        System.out.println(car);
        // 查所有
        List<Car> cars = mapper.selectByExample(null);
        cars.forEach(c -> System.out.println(c));
        // 多条件查询
        // QBC 风格:Query By Criteria 一种查询方式,比较面向对象,看不到sql语句。
        CarExample carExample = new CarExample();
        carExample.createCriteria()
                .andBrandEqualTo("丰田霸道")
                .andGuidePriceGreaterThan(new BigDecimal(60.0));
        carExample.or().andProduceTimeBetween("2000-10-11", "2022-10-11");

        mapper.selectByExample(carExample);
        sqlSession.commit();
    }
}

MyBatis使用PageHelper

limit分页

package com.powernode.mybatis.test;

import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
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.util.List;

public class PageTest {
    @Test
    public void testPage()throws Exception{
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);

        // 页码
        Integer pageNum = 2;
        // 每页显示记录条数
        Integer pageSize = 3;
        // 起始下标
        Integer startIndex = (pageNum - 1) * pageSize;

        List<Car> cars = mapper.selectAllByPage(startIndex, pageSize);
        cars.forEach(car -> System.out.println(car));

        sqlSession.commit();
        sqlSession.close();
    }
}

PageHelper插件

MyBatis的注解式开发

@Insert

@Delete

@Update

@Select

https://www.yuque.com/dujubin/ltckqu/pozck9?singleDoc# 《MyBatis》 密码:rs4n

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值