mybatis02_Mapper代理开发

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

项目结构如下

image-20230302085408248

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

copy
<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接口

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

定义日志输出内容

copy
# 定义输出级别为 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中连接

copy
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对象,所以直接封装成一工具类,让项目启动时就进行加载以便使用。

copy
/** * 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、创建实体类和数据持久层接口、配置映射文件

copy
/** * 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中已经写好了增删改查的一些方法方便直接复制调试。

copy
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); }
copy
<?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

copy
/** * 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,即模糊查询

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

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

copy
<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()将要取的值与 "%" 拼接起来。

copy
<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类的属性名,即实体名。

copy
<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 @   Purearc  阅读(45)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
🚀