mybatis02_Mapper代理开发

1、创建项目并添加依赖、连接数据库,编写mybatis的配置文件

项目结构如下

image-20230302085408248

所需依赖如下(创建的是聚合工程,请根据自己的是实际情况选择合适的版本)

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

        <mybatis.version>3.5.6</mybatis.version>
        <mysql.version>8.0.30</mysql.version>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.18.26</lombok.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--mybatis核心依赖-->
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>${mybatis.version}</version>
            </dependency>

            <!--mysql驱动-->
            <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!--单元测试-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <!--日志-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
        </dependencies>

    </dependencyManagement>

核心配置文件mybatis-config.xml文件如下(当然你可以给他起别的名字,官网上写的是mybatis-config.xml)

在mappers标签中采用的是自动扫描的方式,即会扫描所有com.ls.dao下的xml文件或dao接口

<?xml version="1.0" encoding="GBK"?>
<!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/mybatis?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
<!--        <mapper class="com.ls.dao.BookDao"></mapper>-->
        <package name="com.ls.dao"/>
    </mappers>
</configuration>

定义日志输出内容

# 定义输出级别为 debug,输出名称为 stdout
log4j.rootLogger=debug,stdout
# 定义 stdout 的输出采用哪个类来执行
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 定义 stdout 的输出类型的样式布局
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义 stdout 样式布局的消息格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

创建数据库并在idea中连接

CREATE TABLE `book` (
  `book_id` int NOT NULL AUTO_INCREMENT,
  `book_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `book_author` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`book_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

2、创建工具类MybatisUtil

​ 前面的mybatis简单入门我们已经知道该框架需要通过流加载配置文件mybatis-config.xml创建工厂SqlSessionFactory并使用工厂生产的对象的openSession(打开一个新的session对象,而且每次使用都是打开一个新的session,假如连续使用多次,则获得的session不是同一个对象,并且使用完需要调用close方法关闭session)获取session,这些都是固定的,我们的目的就是获得session对象,所以直接封装成一工具类,让项目启动时就进行加载以便使用。

/**
 * date: 2023/2/25
 *
 * @author Arc
 */
package com.ls.util;

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.InputStream;

public class MybatisUtil {

    static SqlSessionFactory sqlSessionFactory = null;

    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获得SqlSession
     *
     * @return
     */
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

3、创建实体类和数据持久层接口、配置映射文件

/**
 * date: 2023/2/25
 *
 * @author Arc
 */
package com.ls.model;

import lombok.Data;
import lombok.experimental.Accessors;

import java.util.Date;

@Data
@Accessors(chain = true)
public class Book {
    private int bookId;
    private String bookName;
    private String bookAuthor;
    private Date createTime;
    private Date updateTime;
}

在dao中已经写好了增删改查的一些方法方便直接复制调试。

package com.ls.dao;

import com.ls.model.Book;
import java.util.List;

public interface BookDao {

    /**
     * 添加图书
     * @param book
     * @return
     */
    int add(Book book);


    /**
     * 根据id查找
     * @param bookId
     * @return
     */
    Book findById(Integer bookId);

    /**
     * 查询所有
     * @return
     */
    List<Book> findAll();

    /**
     * 根据名字查询所有书籍
     * @param bookName
     * @return
     */
    List<Book> findByName(String bookName);

    /**
     * 根据id删除
     * @param bookId
     * @return
     */
    int deleteById(int bookId);

    /**
     * 根据id修改
     * @param book
     * @return
     */
    int updateById(Book book);
}

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace:全路径接口名称
    id:接口中对应的方法名
    parameterType:方法的入参类型
    (#{bookName}:取值
-->
<mapper namespace="com.ls.dao.BookDao">

    <insert id="add" parameterType="com.ls.model.Book">
-- 返回自增长的主键
-- keyProperty的值应该是实体类对应的名字
        <selectKey keyProperty="bookId" order="AFTER" resultType="int">
            select last_insert_id()
        </selectKey>
        insert into book (book_name,book_author,create_time,update_time)
        values (#{bookName},#{bookAuthor},#{createTime},#{updateTime})
    </insert>

    <select id="findById" parameterType="java.lang.Integer" resultType="com.ls.model.Book">
        select book_id bookId,book_name bookName,book_author bookAuthor,create_time createTime,update_time updateTime
        from book
        where book_id=#{bookId}
    </select>

    <select id="findAll" resultType="com.ls.model.Book">
        select book_id bookId,book_name bookName,book_author bookAuthor,create_time createTime,update_time updateTime
        from book
    </select>

    <select id="findByName" parameterType="java.lang.String" resultType="com.ls.model.Book">
        select book_id bookId,book_name bookName,book_author bookAuthor,create_time createTime,update_time updateTime
        from book
-- 拼接字符串容易造成sql注入,在这里$相当于+
--         where book_name like '%${bookName}%'
        where book_name like concat('%',#{bookName},'%')
    </select>

    <delete id="deleteById" parameterType="java.lang.Integer">
        delete from book where book_id=#{bookId}
    </delete>

    <update id="updateById" parameterType="com.ls.model.Book">
        update book set book_name = #{bookName},book_author= #{bookAuthor},update_time = now()
        where book_id = #{bookId}
    </update>
</mapper>

4、编写mapper映射文件和service层

在mapper代理开发中要求mapper.xml文件的名字要和dao层它对应接口的名字相同,且把二者放到同一目录下,但是我们一般会把配置文件都放到resource目录下,所以我们在resource目录下创建与dao接口对应的目录,注意在创建时要使用 “ / ” 来代替 “ . ” ,否则目录不会分层。

image-20230302092349590

image-20230302094152147

/**
 * date: 2023/2/25
 *
 * @author Arc
 */
package com.ls.service;

import com.ls.dao.BookDao;
import com.ls.model.Book;
import com.ls.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

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

public class BookService {

    public static void main(String[] args) {
//        testInsert();
        testFindAll();
//        testFindById();
//        testFindByName();
//        testDeleteById();
//        testUpdateById();
    }

    /**
     * 测试添加
     */
    public static void testInsert() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtil.getSqlSession();
            //获得BookDao对象
            BookDao bookDao = sqlSession.getMapper(BookDao.class);
            Book book = new Book()
                    .setBookName("bookname").setBookAuthor("author").setCreateTime(new Date()).setUpdateTime(new Date());
            int result = bookDao.add(book);
            System.out.println(result > 0 ? "成功" : "失败");
            sqlSession.commit();
            System.out.println(book);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    /**
     * 测试查找所有
     */
    public static void testFindAll() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtil.getSqlSession();
            //获得BookDao对象
            BookDao bookDao = sqlSession.getMapper(BookDao.class);
            List<Book> books = bookDao.findAll();
            for (Book book1 : books) {
                System.out.println(book1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    /**
     * 测试根据id查找
     */
    public static void testFindById() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtil.getSqlSession();
            //获得BookDao对象
            BookDao bookDao = sqlSession.getMapper(BookDao.class);
            Book book = bookDao.findById(2);
            System.out.println(book);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    /**
     * 测试根据书名进行模糊查询
     */
    public static void testFindByName() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtil.getSqlSession();
            //获得BookDao对象
            BookDao bookDao = sqlSession.getMapper(BookDao.class);
            List<Book> books = bookDao.findByName("my");
            for (Book book : books) {
                System.out.println(book);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    /**
     * 测试根据Id删除
     */
    public static void testDeleteById() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtil.getSqlSession();
            //获得BookDao对象
            BookDao bookDao = sqlSession.getMapper(BookDao.class);
            int result = bookDao.deleteById(1);
            System.out.println(result>0?"成功":"没有找到对应数据");
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    public static void testUpdateById() {
        SqlSession sqlSession = null;
        try {
            sqlSession = MybatisUtil.getSqlSession();
            //获得BookDao对象
            BookDao bookDao = sqlSession.getMapper(BookDao.class);
            Book book = new Book()
                    .setBookId(2).setBookAuthor("junmenmghbai").setBookName("消愁是怎样炼成的");
            int result = bookDao.updateById(book);
            sqlSession.commit();
            System.out.println(result>0?"更新成功":"没有找到对应数据");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }
}

5、一些细节

模糊查询的字符串拼接

​ 在dao层中有一个findByName方法,参数是String类型的书名,要求返回所有含有该参数的Book,即模糊查询

 /**
     * 根据名字查询所有书籍
     * @param bookName
     * @return
     */
    List<Book> findByName(String bookName);

我们知道在sql语句中可以用%来表示任意个字符,所以我们可以使用 "$" 表示拼接字符串,等同于字符串拼接中的 "+"

 <select id="findByName" parameterType="java.lang.String" resultType="com.ls.model.Book">
        select book_id bookId,book_name bookName,book_author bookAuthor,create_time createTime,update_time updateTime
        from book
		where book_name like '%${bookName}%'
    </select>

但是使用字符串拼接如果用户传参不规范就会产生sql注入的问题,所以我们可以使用内置函数concat()将要取的值与 "%" 拼接起来。

 <select id="findByName" parameterType="java.lang.String" resultType="com.ls.model.Book">
        select book_id bookId,book_name bookName,book_author bookAuthor,create_time createTime,update_time updateTime
        from book
        where book_name like concat('%',#{bookName},'%')
    </select>

⬇️两种sql经过编译后

image-20230305144013116

#{ }和$

​ 首先需要了解两个符号的作用 :#{}是一个参数占位符,对于String类型会自动加上"",其他类型不加。由于Mybatis采用预编译,其后的参数不会再进行SQL编译,所以一定程度上防止SQL注入。${}是一个简单的String替换,字符串是什么,解析就是什么。如果在查询的时候我们传入的参数是一个基本数据类型(int、long、字符串等)因为没有其它属性,会用它们的值来作为参数,故在mapper中配置的#{}中可以随便写,但是为了规范,还是推荐按照规范写。

​ 为什么拼接可能会造成sql注入:假如前端传一个bookName值为“java”,对于 where book_name like #{bookName},对应的sql语句就是 where book_name like "java" ;对于 where book_name like ${bookName},对应的sql语句则是where book_name like bookName。这种情况,当用户传参为bookName && 1=1 的时候,就会产生难以预计的后果。

返回自增长主键

​ 如果往某张表中插入一条数据记录,而该表的主键又是自增长的设置,那么在执行 insert 语句 时,我们一般不会对主键列做操作,主键列会自动生成并插入到表中,但是我们可能需要根据主键再去执行相关的修改或者删除操作。

​ 如果表的主键是自增长设置,那么 mysql 在执行 insert 插入提交之前,会自动生成一个自增长主键,并插入到主键列上。获取自增长的主键列的值,可以通过 mysql 的提供的函数 last_insert_id()可以获取到该值,但是此函数的执行必须要在 insert 语句执行之后才可以,所以在 MyBatis 中如果想要获取刚插入的子增长主键记录,则在 mapper.xml 配置文件中,使用如下配置。

这里注意 keyProperty 的值应该是对应POJO类的属性名,即实体名。

<insert id="add" parameterType="com.ls.model.Book">
-- 返回自增长的主键
        <selectKey keyProperty="bookId" order="AFTER" resultType="int">
            select last_insert_id()
        </selectKey>
        insert into book (book_name,book_author,create_time,update_time)
        values (#{bookName},#{bookAuthor},#{createTime},#{updateTime})
    </insert>

⬇️插入成功后返回bookId=9

image-20230305152315068

posted @ 2023-03-08 20:13  Purearc  阅读(42)  评论(0编辑  收藏  举报