day49_mybatis

今日内容

1 引言
2 MyBatis介绍
3 环境搭建
4 入门演示(MyBatis开发步骤)
5 CRUD
6 配置细节

一、引言

1.1 复习

第一阶段(JavaSE)

  • java基本语法
  • 流程控制
  • 面向对象
  • 常用类
  • 集合
  • 异常
  • IO
  • 多线程
  • JDK新特性

第二阶段(JavaWeb)

  • 前端
    • html
    • css
    • js --> JQuery
    • boostrap
    • layui
      • 页面元素
      • 内置模块
  • 服务器端
    • Maven
      • 项目结构(java/javaweb)
      • pom依赖文件
    • Tomcat
    • Servlet
      • 接收请求
      • 做出响应
  • 数据库
    • 基本语句(DDL,DML, DQL) 牛客网-复习
    • 函数
    • 索引,事务,存储引擎
    • JDBC
    • DBUtil
  • 项目
    • 项目整个开发流程
    • 项目结构
    • 项目开发的三层架构
    • 项目中前端后端如何交互
    • 如何调试
      • 输出语句
      • idea - debug
        • 用过之后及时取消
      • 浏览器 - debug
      • 多看日志(控制台,上下翻开)
      • 按照功能逻辑,从头到尾去讲一遍

1.2 框架

SSM --> Spring,SpringMVC,Mybatis , 目前市面很流行的开发框架


什么是框架?

就是为了简化开发步骤,提高开发效率

MyBatis --> 数据访问层框架,把之前的JDBC再封装一下,变得简单易用

SpringMVC --> 控制层框架,基于spring,把之前Servlet再封装一下,变得简单易用

Spring —> 是一个大型容器,是一个"管家",管理项目中所有"对象"(javabean)(不是业务层框架)

特别注意: 框架只是提高开发效率,但是具体业务逻辑,还要自己写

二、Mybatis

2.1 之前的JDBC的使用缺点?

  • 大量的代码重复
  • 手动加载驱动,创建连接(Connection),关流
  • 封装数据麻烦(ORM)
  • 效率不高(没有缓存)

2.2 Mybatis的介绍

官网: mybatis – MyBatis 3 | Introduction

day49_mybatis_第1张图片

MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。用于操作数据库。


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


Mybatis是一个半ORM框架,可以消除JDBC的代码和步骤,让开发者只关注SQL本身。

ORM是对象关系映射,是指数据库表和java实体类一一对应.

半ORM框架,还是需要写SQL,由框架帮你完成映射

完全ORM框架,连SQL都不需要写,只需要遵循ORM的要求,就会自动生成SQL完成映射(Hibernate,JPA等)

三、 MyBatis开发步骤【重点】

官网入门手册中说明如下:

  • 创建项目
  • 加入依赖
  • 执行SQL
    • XML
      • XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)
    • 获得SqlSessionFactoryBuilder
    • 获得SqlSessionFactory
    • 获得SqlSession
    • 执行SQL

自己确定开发步骤

  • 确定表结构和数据
  • 创建项目,加入依赖
  • 确定实体类
  • 写mybatis核心配置文件
  • 写Mapper接口
  • 写Mapper接口对应的映射文件XML
  • 核心配置文件,加载映射文件
  • 通过核心配置文件,获得SqlSessionFactoryBuilder
  • 通过SqlSessionFactoryBuilder 获得 SqlSessionFactory
  • 通过SqlSessionFactory获得SqlSession
  • SqlSession获得接口文件的代理对象
  • 通过代理对象调用方法执行

3.0 数据准备

create database 2307_mybatis;
create table tb_user(
  id int primary key auto_increment,
  username varchar(50),
  password varchar(50),
  phone varchar(50),
  createTime date,
  money double
)default charset = utf8;

3.1 依赖

    <dependencies>
        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.5.6version>
        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.47version>
        dependency>
         
         <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.12version>
        dependency>
    dependencies>

idea安装插件

day49_mybatis_第2张图片

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {

    private int id;
    private String username;
    private String password;
    private String phone;
    private Date createTime;
    private double money;
    private int sex;
}

3.2 写mybatis核心配置文件

名字: 名称任意,一般有这么几种: sqlconfing.xml或者mybatis-config.xml

位置:resources下

内容:(第一次从官网拷贝)


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://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.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/database_name"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
    environments>
    <mappers>
        
    mappers>
configuration>

3.3 写Mapper接口文件

以前需要创建dao层包结构,创建XxxDao接口,以及XxxDaoImpl实现类

现在使用mybatis后,dao层包结构命名为mapper,在包下创建XxxMapper.java接口文件

day49_mybatis_第3张图片

3.4 写Mapper映射文件

  • 为什么要?

    • 以前是写接口的实现类,自己在类中自己写JDBC步骤,完成CRUD操作;现在,需要使用Mybatis的映射文件,我们需要在配置文件中写sql,剩下的动作mybatis完成.即不需要再写实现类.而是需要创建一个接口的映射文件
  • 写到哪?

  • 一种写到resources下 (今天用这种)

  • 另一种,可以写在com.qf.mapper包下(后续再用)

  • 名字?

    • 与接口文件一样UserMapper.xml
  • 怎么写?如下


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.qf.mapper.UserMapper">

    
    <select id="findUserById" resultType="com.qf.model.User">
        
        
        select * from tb_user where id = #{id}
    select>
mapper>

3.5 核心配置文件得加载映射文件

day49_mybatis_第4张图片

3.6 测试

package com.qf.test;

import com.qf.mapper.UserMapper;
import com.qf.model.User;
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;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc
 */
public class TestMybatis {

    // TODO 优化
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 不用背,知道有这一步,下次直接复制
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 获得SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession( );


        // BlogMapper mapper = session.getMapper(BlogMapper.class);
        // Blog blog = mapper.selectBlog(101);

        // 利用的动态代理技术,获得UserMapper的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 调用方法干活
        User user = userMapper.findUserById(1);
        System.out.println(user);
    }
}

四、 单元测试【熟练】

以前测试代码,都需要创建主方法,但是一个主方法只能完成一个功能! 再有新功能,就需要抽取方法,再继续再main方法中再写 —>这些写很麻烦


测试,就可以使用单元测试来测试代码,每个方法都可以独立执行,还可以抽取一些公用代码,减少重复

4.1 加入依赖


<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.13version>
    
    <scope>testscope>
dependency>

4.2 使用

在测试包下,创建类,定义方法,加上注解即可使用

// 方法不能是static,不能有返回值,不能有参数
@Test
public void selectById() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 不用背,知道有这一步,下次直接复制
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    // 获得SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession( );


    // BlogMapper mapper = session.getMapper(BlogMapper.class);
    // Blog blog = mapper.selectBlog(101);

    // 利用的动态代理技术,获得UserMapper的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    // 调用方法干活
    User user = userMapper.findUserById(1);
    System.out.println(user);
}

4.3 抽取公共代码

SqlSession sqlSession = null;

@Before  // 该注解一加,该方法就会在每个单元测试方法【执行前】执行
public void init() throws IOException {
    System.out.println("前前前前前前" );
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 不用背,知道有这一步,下次直接复制
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    // 获得SqlSession
    sqlSession = sqlSessionFactory.openSession( );
}

五、 CRUD

5.1 查询

5.1.1 单条件查询

略,详情看入门演示

【特别注意:ORM时字段名要和实体类属性一致,否则封装失败】

<select id="findUserById" resultType="com.qf.model.User">
    
    
    
    
    
    select id,username,password,phone,createTime,sex,money from tb_user where id = #{id}
select>

5.1.2 查询全部

设计查询接口方法

public interface UserMapper {
    List<User> findAll();
}

映射文件

    
    
    
    <select id="findAll" resultType="com.qf.model.User">
        select * from tb_user
    select>

测试

@Test
public void selectAll() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 不用背,知道有这一步,下次直接复制
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    // 获得SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession( );

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    List<User> list = mapper.findAll( );
    for (User user : list) {
        System.out.println(user );
    }
}

5.1.3 多参数查询

需求: 通过用户名和密码查询

接口方法

public interface UserMapper {
    User findUserByLogin(String username,String password);
}

映射文件

<select id="findUserByLogin" resultType="com.qf.model.User">
    
    
    select * from tb_user where username = #{param1} and password = #{param2}
select>



接口方法(参数加注解)

public interface UserMapper {

    User findUserByLogin(@Param("username") String username, @Param("password") String password);

}

映射文件

    <select id="findUserByLogin" resultType="com.qf.model.User">
        
        
        select * from tb_user where username = #{username} and password = #{password}
    select>

5.1.4 Map参数查询

需求: 查询时,就要传递分页数据,又要传递模糊查询关键词,此时就可以使用Map来封装参数.

接口方法

public interface UserMapper {
    User findUserByLoginMap(HashMap<String,Object> map);
}

映射文件

<select id="findUserByLoginMap" resultType="com.qf.model.User">
    
    select * from tb_user where username = #{usernameKey} and password = #{passwordKey}
select>

测试

@Test
public void selectByLoginMap() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // 不用背,知道有这一步,下次直接复制
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    // 获得SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession( );

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    HashMap<String, Object> map = new HashMap<>( );
    map.put("usernameKey","李豪");
    map.put("passwordKey","123456");
    User user = mapper.findUserByLoginMap(map);
    System.out.println("user = " + user);
}

5.2 增加

接口方法

public interface UserMapper {
    int addUser(User user);
}

映射文件

    
    <insert id="addUser">
        
        
        
        
        insert into tb_user values (#{id},#{username},#{password},#{phone},#{createTime},#{money},#{sex});
    insert>

测试

    @Test
    public void addUser() throws IOException {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = new User( );
        user.setId(4);
        user.setUsername("谢博");
        user.setPassword("123456");
        user.setPhone("1111");
        user.setcreateTime(new Date());
        user.setSex(1);
        user.setMoney(10000 );
        int i = mapper.addUser(user);

        System.out.println(i > 0 ? "插入成功":"失败");

        // mysql默认的事务是一句话就是一个独立的事务,自动提交
        // mybatis封装后,默认事务没有自动提交
        // 所以执行增删改需要手动提交事务
        sqlSession.commit();
    }

5.3 增加后主键回填

场景: 下订单,返回订单号

需求: 插入数据后,要得到数据库自增的id

实现: mysql提供一个sql语句select last_insert_id();可以获得上次自增的id

    int insertUser(User user);
    <insert id="insertUser" parameterType="com.qf.model.User">
        insert into user (username,password,createTime,phone,address)
        values(#{username},#{password},#{createTime},#{phone},#{address})

        
        <selectKey keyProperty="id" order="AFTER" resultType="int">
            select last_insert_id()
        selectKey>

    insert>
   /**
     * 测试更新
     */
    @Test
    public void testInsert() throws IOException {
        // 加载配置文件,变成流
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

        // 通过流获得工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder( ).build(inputStream);

        // 通过工厂SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession( );

        // 通过SqlSession获得接口的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        // 创建一个对象
        User user = new User( );
        user.setUsername("喻去子");
        user.setPassword("123456");
        user.setAddress("中国 贵州 黔南布依族苗族自治州");
        user.setcreateTime(new Date());

        // 执行插入,其中会有主键回填
        System.out.println("插入前: " +user );
        int i = mapper.insertUser(user);
        System.out.println(i );

        // 提交
        sqlSession.commit();

        // 再次打印对象,此时对象中的id有值
        System.out.println("插入后: " +user );
    }

5.4 修改

接口方法

public interface UserMapper {
    int updateUser(User user);
}

映射文件

    <update id="updateUser">
        
        update tb_user set username=#{username},password=#{password},
        phone= #{phone},createTime=#{createTime},money=#{money},sex=#{sex}
        where id = #{id}
    update>

测试

    @Test
    public void updateUser() throws IOException {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = new User( );
        user.setId(4);
        user.setUsername("谢大博");
        user.setPassword("123456");
        user.setPhone("22222");
        user.setcreateTime(new Date());
        user.setSex(2);
        user.setMoney(20000 );
        int i = mapper.updateUser(user);

        System.out.println(i > 0 ? "更新成功":"失败");

        // mysql默认的事务是一句话就是一个独立的事务,自动提交
        // mybatis封装后,默认事务没有自动提交
        // 所以执行增删改需要手动提交事务
        sqlSession.commit();
    }

5.5 删除

自己写

六、细节补充


6.1 解决mapper.xml存放在resources以外路径中的读取问题

Mapper.xml文件除了放在resources下,也可以放在其他位置,常见的会和接口放在一起

day49_mybatis_第5张图片

如上这样,那么在mybatis-config.xml中就可以这么写

    <mappers>
        <mapper resource="com/qf/mapper/UserMapper.xml"/>
    mappers>

但是,IDEA默认不会加载这个UserMapper.xml,需要在POM.xml加入如下配置

在pom.xml文件最后追加< build >标签,以便可以将xml文件复制到classes中,并在程序运行时正确读取。

<build>

    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
              	<include>*.xmlinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>truefiltering>
        resource>
    resources>
build>

如上配置,随着项目模块功能的增多,接口也多了起来,随之对应的映射文件也多了起来,如下

<mappers>
 <mapper resource="com/qf/model/DogMapper.xml"/>
 <mapper resource="com/qf/model/PigMapper.xml"/>
 <mapper resource="com/qf/model/CatMapper.xml"/>
    ...
    ...
mappers>

怎么解决呢?

mybatis的全局配置文件提供了一个简单写法

    <mappers>
        <package name="com.qf.mapper"/>
    mappers>

6.2 properties配置文件

对于mybatis-config.xml的核心配置中,如果存在需要频繁改动的数据内容,可以提取到properties中。

#在resources下创建db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncpding=utf8
jdbc.username=root
jdbc.password=123456

修改mybatis-config.xml。


DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	
    <properties resource="db.properties" />

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <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}"/>
            dataSource>
        environment>
    environments>
    
configuration>

6.3 类型别名

为实体类定义别名,提高书写效率。

在映射XML文件时,resultType中的类名是全路径,设置了别名就可以简单只写类名即可


DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <properties ... />
    
    
    <typeAliases>
        
        

        
        <package name="com.qf.model"/>
    typeAliases>
  
  	...
configuration>

6.4 下换线转驼峰

场景描述:

数据库有个字段create_time,mybatis查询时封装数据到User类中时,User类的属性createTime没有值,是null , 为什么??

原因:

​ 就是因为数据库列create_time和属性名createTime不一致,导致封装失败

解决方案:

​ 开启mybatis中,数据库列,下划线转驼峰

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    settings>

官方解释:是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn, 默认是false,需要指定成true开启

映射文件,只写类名即可

	<select id="findUserById" resultType="User">
        select * from tb_user where id = #{id}
    select>

6.5 创建log4j配置文件

pom.xml添加log4j依赖


<dependency>
		<groupId>log4jgroupId>
		<artifactId>log4jartifactId>
		<version>1.2.17version>
dependency>

创建在resources并命名为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

特别注意: 可以配置只打印sql

log4j.rootLogger=info
# 设置mapper接口类或者配置文件中mapper的路径为debug
log4j.logger.com.qf.mapper=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
级别 描述
ALL LEVEL 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。
DEBUG 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。
INFO 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。
WARN 输出警告信息;表明会出现潜在错误的情形。
ERROR 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。
OFF LEVEL 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。

mybatis-config.xml设置使用日志

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    settings>

然后在执行mybatis代码的是就会有日志输出

作业

练习
	1 代码重复2遍
作业
	2 换一张表,再来一遍
思考
	思考MyBatis到底帮你省了哪些事情?
	接口文件和映射文件如何关联?
	接口文件中方法又是如何和映射文件中的语句关联?
	语句执行时入参都可以有哪些?有什么注意事项?
	语句执行后返回的有哪些类型?

he.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

级别 描述
ALL LEVEL 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。
DEBUG 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。
INFO 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。
WARN 输出警告信息;表明会出现潜在错误的情形。
ERROR 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。
OFF LEVEL 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。

mybatis-config.xml设置使用日志

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    settings>

然后在执行mybatis代码的是就会有日志输出

作业

练习
	1 代码重复2遍
作业
	2 换一张表,再来一遍
思考
	思考MyBatis到底帮你省了哪些事情?
	接口文件和映射文件如何关联?
	接口文件中方法又是如何和映射文件中的语句关联?
	语句执行时入参都可以有哪些?有什么注意事项?
	语句执行后返回的有哪些类型?

你可能感兴趣的:(#,Java2307,mybatis,java,开发语言)