Mybatis笔记(视频是动力节点王妈妈10.5小时)

简介

Mybatis,原名iBatis,是Apache开源项目,10年迁移到google code改名Mybatis,13年迁移到github。是dao层的框架,用于数据库访问的各操作。

JDBC的缺点

需要创建很多对象、注册驱动、关闭资源这些重复性的代码,费事;

sql语句与业务逻辑代码混在一起

quick start(查询)

先在数据库建张表,不需要多复杂,重在体会过程。还用之前的老例子

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JVa6q48N-1639327618695)(/../Mybatis(尚硅谷王妈十个半小时).assets/image-20211212012856537.png)]

引入必要依赖:

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
    </dependency>
</dependencies>

在entity(或domain、pojo)包下创建实体类,其属性对应表中字段名,只不过数据库是“_”连接多个单词,属性名是驼峰命名法。并生成对应属性的get、set方法。

然后创建dao类,并在同一个包下创建与该类同名的xml映射(mapper)文件

<?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="ind.deng.mybatis.dao.BookDao">
    <select id="selectBookById" resultType="ind.deng.mybatis.entity.Book">
        select * from book where id = #{bookId}
    </select>
</mapper>

前面四行是固定语法,每次创建mapper文件直接copy即可。

其中的"http://mybatis.org/dtd/mybatis-3-mapper.dtd"是约束文件,限定当前文件内可以使用的标签和属性及其前后顺序

mapper跟标签namespace属性必须有,不能为空,其值为对应dao接口的全限定名(约定)

mapper标签里可以有,,,标签

id值为dao中的方法名(约定),resultType值为对应实体类的全限定名,查询操作才有resultType

注意,sql语句末尾没有分号。#{bookId}是占位符,接收传递过来的参数

注:#{}占位符底层使用的是preparedStatement,效率高且防止sql注入,也能根据参数类型而变化,即string类型也不用自己加单引号

还有另一种KaTeX parse error: Expected 'EOF', got '#' at position 10: {}占位符,功能和#̲{}一致,但底层使用state…{}中的内容和sql语句拼接,类似c语言中的宏定义。

效率低且有注入风险。此外,${}就算只有一个普通参数也得用@Param

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

mapper文件在dao包里(在resources下就没这问题)。要想让编译器找到,需要在pom文件加上以上几行代码(和dependencies平级)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="iquygned787923"/>
            </dataSource>
        </environment>
    </environments>
        <mappers>
            <mapper resource="ind/deng/mybatis/dao/BookDao.xml"/>
        </mappers>
</configuration>

这是mybatis核心配置文件,一般放在resources下。与mapper文件前四行不同的地方在于第二行的configuration和第四行的mybatis-3-config,copy过来改一下就行。

根标签是configuration(和第二行对应)。其内的environment标签暂时不知道什么作用,反正在其内配置数据源,老生常谈了。

与environments同级的有个mappers标签,里面通过mapper标签指定mapper文件的位置,resource值为mapper文件的类路径,注意事项‘/’而不是‘.’。有多个mapper文件就要写多个mapper标签

mybatis一般使用tomcat内置的commons-logging。标签内可以改成别的如log4j

@Test
public void test01() throws IOException {
    String config = "mybatis.xml";
    InputStream stream = Resources.getResourceAsStream(config);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
    SqlSession sqlSession = factory.openSession();
    String sqlId="ind.deng.mybatis.dao.BookDao.selectBookById";
    Book book = sqlSession.selectOne(sqlId,1);
    System.out.println(book);
    sqlSession.close();
}

Test方法。先加载mybatis核心配置文件,获取SqlSessionFactory对象并用其openSession获取一个SqlSession对象,用这个对象执行sql语句。感觉还是很繁琐,后边应该有简单的方法(注解?)

selectOne可以只传递sql语句,也可以在后面加个数不定的参数

测试插入

测试插入。此时的占位符有两个及以上的参数,一般接收的是一个对象,所以占位符内的属性名和对应实体类的属性名是一致的。在dao新建方法时,按alt+enter可以快速生成mapper文件中的标签

<insert id="insertBook">
        insert into book (name,price) values(#{name},#{price})
</insert>

增删改会修改数据库,而mybatis事务默认提交方式是手动提交。必须加上sqlSession.commit()语句数据库才有变化。其他语句大同小异

@Test
public void test02() throws IOException {
    String config = "mybatis.xml";
    InputStream stream = Resources.getResourceAsStream(config);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
    SqlSession sqlSession = factory.openSession();
    String sqlId="ind.deng.mybatis.dao.BookDao.insertBook";
    Book book = new Book();
    book.setName("大学物理");
    book.setPrice(51.9);
    int rows = sqlSession.insert(sqlId,book);
    System.out.println(rows);
    sqlSession.commit();
    sqlSession.close();
}

总结重要对象的作用

Resources:读取配置文件,传给InputStream

SqlSessionFactoryBuilder:根据输入流创建SqlSessionFactory对象

SqlSessionFactory是重量级对象,创建很消耗资源和时间,所以一个项目中有一个就行了。它是一个接口,其实现类是DefaultSqlSessionFactory

SqlSessionFactory中的openSession方法创建SqlSession对象,可以传入bool值,如果为true代表自动提交事务,false则跟不传一样

SqlSession本身是个接口,提供大量的操作sql语句的方法(增删改查(一/全)提交回滚)。DefaultSqlSession是其实现类

SqlSession不是线程安全的,所以需要再一个方法内(局部)使用

使用工具类和模板

添加mapper文件和mybatis核心配置文件的模板

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-esO7C2GA-1639327618697)(/../Mybatis(尚硅谷王妈十个半小时).assets/image-20211212143540341.png)]

把创建sqlSession的过程封装成工具类:

private static SqlSessionFactory factory = null;
    static {
        String config = "mybatis.xml";
        try {
            InputStream stream = Resources.getResourceAsStream(config);
            factory = new SqlSessionFactoryBuilder().build(stream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
        SqlSession session = null;
        if(factory!=null){
            session = factory.openSession();
        }
        return session;
    }
}

使用工具类:

@Test
public void test03() throws IOException {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    String sqlId="ind.deng.mybatis.dao.BookDao.selectBookById";
    Book book = sqlSession.selectOne(sqlId,2);
    System.out.println(book);
    sqlSession.close();
}

代码量少了一些。当sql语句非常多时,可就不止少了一些了

dao实现类

以上写的代码dao接口似乎没什么用,所以应该为dao接口编写实现类,调用实现类的方法:

public class BookDaoImpl implements BookDao {
    @Override
    public Book selectBookById(Integer id) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        String sqlId="ind.deng.mybatis.dao.BookDao.selectBookById";
        Book book = sqlSession.selectOne(sqlId,id);
        return book;
    }

    @Override
    public int insertBook(Book book) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        String sqlId="ind.deng.mybatis.dao.BookDao.insertBook";
        int rows = sqlSession.insert(sqlId,book);
        System.out.println(rows);
        sqlSession.commit();
        sqlSession.close();
        return rows;
    }
}

测试:

@Test
public void test04() {
    BookDao bookDao = new BookDaoImpl();
    Book book = bookDao.selectBookById(2);
    System.out.println(book);
}
@Test
public void test05()  {
    BookDao bookDao = new BookDaoImpl();
    Book book = new Book();
    book.setName("亲热天堂");
    book.setPrice(99.9);
    int rows = bookDao.insertBook(book);
    System.out.println(book);
}

dao代理

然而,mybatis根据dao方法的调用,可以得到sqlId(通过反射知道dao接口全限定名,拼接上dao方法便得到sqlId)。然后通过mapper文件的标签得知是增删改查中的那个方法,通过返回值是对象还是集合确定是selectOne还是selectList。

mybatis得知了这些必要信息,所以利用动态代理帮我们在内存中生成dao接口的实现类(代理类)

使用dao代理的前提条件(之前说过):namespace值为dao接口全限定名称;id值为接口对应方法名称

使用:

@Test
public void test04() {
    SqlSession session = MyBatisUtil.getSqlSession();
    BookDao bookDao = session.getMapper(BookDao.class);
    Book book = bookDao.selectBookById(2);
    System.out.println(book);
}
@Test
public void test05()  {
    SqlSession session = MyBatisUtil.getSqlSession();
    BookDao bookDao = session.getMapper(BookDao.class);
    Book book = new Book();
    book.setName("亲热天堂");
    book.setPrice(99.9);
    int rows = bookDao.insertBook(book);
    session.commit();
    System.out.println(book);
}

通过session.getMapper(dao接口.class)获得dao接口的对象

parameterType

指定dao方法的形参类型,比如形参类型是Integer,则parameterType=“java.lang.Integer”。但这些值都有别名,java.lang.Integer的别名有int和integer,所以用int即可。mybatis通过反射机制可以知道形参类型,所以parameterType可以不写。只是写了之后可读性更高

dao方法形参

当形参为简单类型(内置类型和String)时,sql语句中用占位符#{}接收参数

有多个简单类型参数时,需要使用@Param注解

dao中的方法:

public List<Book> selectByIdOrPrice(@Param("id") Integer id, @Param("price") Double price);

mapper文件中仍然使用#{}接收参数

<select id="selectByIdOrPrice" resultType="ind.deng.mybatis.entity.Book">
    select * from book where id=#{id} or price >#{price}
</select>

测试:

@Test
public void test04() {
    SqlSession session = MyBatisUtil.getSqlSession();
    BookDao bookDao = session.getMapper(BookDao.class);
    List<Book> books = bookDao.selectByIdOrPrice(3,30.0);
    books.forEach(book -> System.out.println(book));
}

books.forEach(book -> System.out.println(book));用到了lambda表达式,遍历集合,用和books同类型的book变量接收集合中的每个对象,箭头右边是执行的语句。箭头左边如果有多个参数用()括起来,逗号分隔

这种方法当形参很多时太臃肿,所以一般传对象作为参数,对应的xml文件中#{}中和对象的属性名一致即可。所以我上边的xml文件不需要变化,把dao方法中的形参换为Book book即可

也可以按位置接收形参:id=#{args0} or price >#{args1}

不推荐使用,可读性不好

${}占位符

KaTeX parse error: Expected 'EOF', got '#' at position 10: {}占位符,功能和#̲{}一致,但底层使用state…{}中的内容和sql语句拼接,类似c语言中的宏定义。

效率低且有注入风险。此外,${}就算只有一个普通参数也得用@Param。

但如果想根据传过来的字符串判断按表中哪一列排序,#{}就不好使了,因为会自动给传过来的字符串加上’'比如

select * from book order by ‘id’

这样查出来的数据原封不动,并不会按id排列。要想使得

select * from book order by id

${}就派上用场了,忠实的将传过来的数据拼接

可以在一个sql语句中和#{}同时使用

ResultType

用于select标签中,其值可以是类的全限定名,把查询到的结果集封装为java对象

底层是mybatis利用反射创建该对象,并把查询到的同名列值赋给对象的同名属性。所以数据表字段要和类的属性一一对应

如果dao方法返回值是集合类型,ResultType仍然是对应的对象全类名,然后把查询到的全部对象放入集合中

ResultType也可以表示简单类型。场景:

<select id="selectCount" resultType="java.lang.Integer">
    select count(*) from book
</select>

ResultType为Map

public Map<Object,Object> selectMap(@Param("id") Integer id);
<select id="selectMap" resultType="java.util.Map">
    select name,price from book where id=#{id}
</select>

查询结果只能是一行数据,将这行数据搜索的属性(name、price)放入map中。多行数据会报错

别名

可以在mybatis主配置文件中为类型定义别名。标签在标签下方

<typeAliases>
    <typeAlias type="ind.deng.mybatis.entity.Book" alias="book"/>
</typeAliases>

也可以

<typeAliases>
    <package name="ind.deng.mybatis.entity"/>
</typeAliases>

这样这个包内的类的类名就是自己的别名,不区分大小写

不用别名最好

ResultMap

自定义数据表列名和对象属性名的对应关系

<resultMap id="bookMap" type="ind.deng.mybatis.entity.Book">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="price" property="price"/>
</resultMap>

<select id="selectBookById" resultMap="bookMap">
    select * from book where id = #{bookId}
</select>

resultMap标签内id对应表中主键,result对应其他字段

column是数据表字段名,property是对象属性名,resultMap就是将两者映射起来的

如果两者值一致,可以不设置

后面的select标签就可以把resultType替换为resultMap。

resultMap可复用,开发中经常使用

resultType也可以解决字段名和属性名不一致的问题,即给列取别名(麻烦)

模糊查询

dao

public List<Book> fuzzySelect(String name);

xml

<select id="fuzzySelect" resultType="ind.deng.mybatis.entity.Book">
    select * from book where name like #{name}
</select>

test

@Test
public void test09()  {
    SqlSession session = MyBatisUtil.getSqlSession();
    BookDao bookDao = session.getMapper(BookDao.class);
    List<Book> books = bookDao.fuzzySelect("%数%");
    books.forEach(book -> System.out.println(book));
    session.close();
}

一般在java程序而不是sql语句中组织内容(即%的位置),不然sql频繁改动,不合理

动态sql

同一个dao方法,根据不同条件可以表示不同sql语句,主要是where部分有变化。dao方法的形参是java对象.常用于多条件查询。

if

public List<Book> selectIf(Book book);

if语句内是和对象的属性有关的条件

<select id="selectIf" resultType="ind.deng.mybatis.entity.Book">
    select * from book where
    <if test="name!=null and name!=''">
        name=#{name}
    </if>
    <if test="price>0">
        or price>#{price}
    </if>
</select>

可以有多个if,但没有else语句。别忘了加and或or等连接sql语句的连接词。

但有个小细节,如果name为空,sql语句就变成了select * from book where or price>?

这个’or’就很突兀,语法错误

所以可以这样写:

<select id="selectIf" resultType="ind.deng.mybatis.entity.Book">
    select * from book where 1!=1
    <if test="name!=null and name!=''">
        or name=#{name}
    </if>
    <if test="price>0">
        or price>#{price}
    </if>
</select>

xml文件中有时候会用大于小于号,和标签符号冲突,应该使用字符实体& l t ; & g t ;

where

if一般和where结合使用

<select id="selectIf" resultType="ind.deng.mybatis.entity.Book">
    select * from book
    <where>
        <if test="name!=null and name!=''">
            name=#{name}
        </if>
        <if test="price>0">
            or price>#{price}
        </if>
    </where>
</select>

if条件有一个满足时,语句后加上where;全都不满足时,不加where

where会删除紧跟在where其后的and或or,因此不会语法错误

foreach

dao

public List<Book> selectForeach(List<Integer> ids);

xml

 <select id="selectForeach" resultType="ind.deng.mybatis.entity.Book">
     select * from book
     <if test="list!=null and list.size > 0">
         where id in
         <foreach collection="list" open="(" close=")" separator="," item="ids">
             #{ids}
         </foreach>
     </if>
</select>

collection的值,数组为array,list就是list。其他属性见名知意

if语句提高健壮性

foreach遍历对象集合:

public List<Book> selectForeach2(List<Book> books);
<select id="selectForeach2" resultType="ind.deng.mybatis.entity.Book">
    select * from book
    <if test="list!=null and list.size > 0">
        where id in
        <foreach collection="list" open="(" close=")" separator="," item="book">
            #{book.id}
        </foreach>
    </if>
</select>

book对应集合中的每个对象,再获得每个对象中的属性值

sql

提取出公共代码片段,在需要的地方引用,实现代码复用

<sql id="selectBook">select * from book</sql>
<select id="selectBookById" resultMap="bookMap">
    <include refid="selectBook"></include>
    where id = #{bookId}
</select>

感觉很鸡肋,可读性也不好。sql语句原来就不长,复杂的sql也没有多少公共部分

主配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
        <mappers>
            <mapper resource="ind/deng/mybatis/dao/BookDao.xml"/>
        </mappers>
</configuration>

settings是mybatis的全局设置,一般默认值即可

用来设置别名,见"别名"内容

是环境标签,可以配置多个环境,一个环境对应一个数据库的连接信息。default取值对应下面环境的id,实现切换不同的环境

id值任意,表示一个环境,一般使用development、online、test等有含义的名字

type还可以取值MANAGED,用于spring,让容器管理事务而不是mybatis

type可以取UNPOOLED(不用数据库连接池)、JNDI(不知道啥东西,现在不用了)

数据库的配置信息可以抽取出来:

jdbc.properties:

driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root

mybatis主配置文件:

<!--引入properties配置文件-->
<properties resource="jdbc.properties"/>
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"></transactionManager>
        <dataSource type="POOLED">
            <!--用${}接收配置文件中键对应的值-->
            <property name="driver" value="${driverClass}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

用来指定mapper文件的位置

属性为resource时,值为xml文件的全限定名,精确,但数量多时写起来麻烦

属性为name时,只需给出包名(一般都在dao下),该包下的所有xml文件都会被确定。前提条件:mapper文件和对应的dao接口在同一目录下;mapper文件和dao接口名称一致

PageHelper分页插件

先引入依赖:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.0</version>
</dependency>

在mybatis主配置文件中之前加入

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

测试:

@Test
public void test06()  {
    SqlSession session = MyBatisUtil.getSqlSession();
    BookDao bookDao = session.getMapper(BookDao.class);
    PageHelper.startPage(1,3);
    List<Book> books = bookDao.selectBooksOrderByColumnName("id");
    books.forEach(book -> System.out.println(book));
    session.close();
}

PageHelper.startPage(1,3);第一个参数对应当前显示第几页,从1开始;第二个参数对应每页的记录数

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vogel_im_Kafig_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值