MyBatis

4 篇文章 0 订阅

简介

MyBatis是一款持久层框架,用于简化JDBC开发

持久层:负责将数据保存到数据库的那一层代码

JavaEE三层架构:表现层、业务层、持久层

框架:一个半成品软件,是一套可重用的、通用的、软件基础代码模型

作用

MyBatis的主要作用为解决硬编码问题和操作繁琐问题

硬编码:在编写代码时由于代码内容可能与地址、用户名、密码等相关,因此可能会发生变动,后续维护困难

如JDBC操作时mysql中使用的数据库发生变化,用户名或密码发生变化

SQL语句的变化

操作繁琐:

如JDBC中执行sql语句时可能需要手动设置参数,结果集需要手动获取并装入集合

入门

1.导入jar包

使用MyBatis时,首先需要导入它的jar包,使用Maven则对应坐标为:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>
<!--version需要手动更改版本-->

2.编写MyBatis核心配置文件

为解决硬编码问题,MyBatis将涉及硬编码的字符串信息全部装到了xml文件中,届时若需要更改维护则只需更改xml文件即可

在main包下的resources下创建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="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

其中,需要更改的内容为数据库的连接信息以及sql映射文件的名称

3.编写SQL映射文件

为解决sql的硬编码问题,相应sql也需要写到一个xml文件中

在main包下的resources下创建xml文件(名称一般为表名 + Mapper.xml,如UserMapper.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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

其中,需要更改的信息有

namespace:命名空间,可以储存一系列的sql语句,可以理解为一个包,届时使用sql语句时可以通过命名空间和id来找到对应sqs

id:sql语句的唯一标识,通过id可以找到对应sql,以便后续执行

resultType:结果类型,代表执行sql后返回的结果的数据类型

select语句

注:不同命名空间中id可以相同,在调用sql语句时有两种方式

全限定名:命名空间 + id

短名称:id

在id无重复时短名称可以正常使用,但当id在不同命名空间中重复出现时使用短名称会出错,因此一般使用全限定名

在编写好SQL映射文件后需要将该文件的名称填入到MyBatis核心配置文件中的resource中

4.编码

在解决完上述前置问题后,可以正式开始编写代码

4.1定义POJO类

定义一个类来代表相应的表

如查询User表,则需要定义User类并封装相应的字段信息

4.2加载核心配置文件,获取SqlSessionFactory对象

在main方法中复制以下代码

String resource = "mybatis-config.xml";//这里为核心配置文件的相对路径
InputStream inputStream = Resources.getResourceAsStream(resource);//需要抛出异常
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

最终得到sqlSessionFactory对象

4.3获取SqlSession对象,执行sql语句

使用sqlSessionFactory的方法openSession得到sqlSession对象

SqlSession sqlSession = sqlSessionFactory.openSession();

SqlSession sqlSession = sqlSessionFactory.openSession(true);//可以设置参数为true,开启自动提交事务

通过sqlSession对象执行sql语句

List selectList(String xxx)

该方法得到的结果为List集合,对应的sql语句应为查询了多个数据

Xxx selectOne(String xxx)

该方法得到的为单个对象,对应的sql语句应为查询了单个数据

其中,需要的参数为sql语句的全限定名或短名称

通过上述方法即可执行sql并得到结果集

最终释放资源即可

在idea中配置MySql数据库连接

在编辑SQL映射文件中sql语句时由于idea无法读取信息,会出现表名爆红的情况,可以将idea与MySQL数据库连接解决

1.找到Database

2.找到MySQL

3.建立连接

填写用户名、密码、连接的数据库即可

在配置完连接后,后续编写sql语句时也会有提示功能

插件下载

ieda中可以下载一个MyBatisX插件,在使用MyBatis时可以更为便捷

Mapper代理开发

目的

解决原生方式中的硬编码(全限定名)

简化后期执行SQL

步骤

1.定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下

正常的main包在编译过后,java包下的内容和resources下的内容会放在一起,且因为xml文件一般放置在resources中,所以想要接口和映射文件在同一目录,需要在resources中建立包名和结构与接口相同的包,并将映射文件放置进去

如图,UserMapper接口所在包的结构与对应的映射文件所在结构相同,在编译后它们会放置在同一目录下

注:

1.resources创建时不存在包这个概念,只有文件夹,创建时直接创建文件夹即可,且idea中创建文件夹时需要用com/itheima//mapper的形式来创建,这样创建出的文件夹才是分层的结构

2.在更改完映射文件的位置后,MyBatis核心配置文件中sql映射文件的地址也需要对应修改

2.设置SQL映射文件的namespace属性为Mapper接口全限定名

3.在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致

注:返回的数据若为多个User对象,则这里返回值类型也需要改为List集合,这样到时候调用方法时会自动调用selectList方法,而不是selectOne

4.编码

4.1通过SqlSession的getMapper方法获取Mapper接口的代理对象

4.2调用对应方法完成sql的执行

5.提交事务,关闭资源

若调用的方法为增删改,则在完成sql后需要提交事务

调用完方法后,需要关闭资源

注:

如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以通过使用包扫描 方式简化SQL映射文件的加载

如图,核心配置文件中加载sql映射文件时,可以直接加载mapper包(因为所有的映射文件和对应接口都会放在该包下)

Mapper接口中方法参数的传递

Mapper接口中的方法可以接收参数,到时候会传递给sql语句中的参数占位符,当传递的参数为多个时,

有以下三种传递方式

1.设置多个参数

使用@Param注解,到时候该参数会传递给名称和注解相同的参数占位符

2.传递对象

若传递的参数属于某一个类的属性,则可以直接传递一个对象

例:

Brand类里包含status、companyName等多个属性,则可以将这几个参数包装成一个Brand类传递给sql语句,到时候会到该对象里寻找与参数占位符名称相同的属性,并将其传递给参数占位符

3.传递集合

将参数包装成一个Map集合,键与参数名称相同,值为参数值,将其传递给sql语句

Mapper接口中方法参数的传递的底层原理

1.单个参数

1.1 POJO类型

直接使用,但要求属性名和参数占位符名称一致

注:在从pojo类中读写信息时,mybatis读取属性 读取的是get和set方法,如要读取user类中的id信息时,其会调用getId方法,无论此时是否有成员变量id。读取属性的过程为将get,set方法中的get和set去掉,剩余部分首字母变小写,得到的就是属性名

1.2 Map集合

直接使用,但要求键名和参数占位符名称一致

1.3 Collection

封装为map集合

其中键值对为:

map.put("arg0",collection集合);

map.put("collection",collection集合);

可以使用使用@Param注解,使用该注解后会替换Map集合中默认的arg键名

1.4 List

封装为map集合

其中键值对为:

map.put("arg0",list集合);

map.put("collection",list集合);

map.put("list",list集合);

可以使用使用@Param注解,使用该注解后会替换Map集合中默认的arg键名

1.5 Array

封装为map集合

其中键值对为:

map.put("arg0",数组);

map.put("array",数组);

可以使用使用@Param注解,使用该注解后会替换Map集合中默认的arg键名

1.6 其他类型

直接使用,且因为只有一个参数,因此参数名随意,参数占位符的名称随意

2.多个参数

传递多个参数时会将这些参数封装成Map集合

其中键值对为:

map.put("arg0",参数值1);

map.put("param1",参数值1);

map.put("arg1",参数值2);

map.put("param2",参数值2);

...

即每个参数都会创建两个键值对,但这些键值对的键名使用不方便,因此使用@Param注解,使用该注解后会替换Map集合中默认的arg键名

即变为

map.put(参数1的名称,参数值1);

map.put("param1",参数值1);

map.put(参数2的名称,参数值2);

map.put("param2",参数值2);

...

MyBatis核心配置文件

环境配置(environments)

environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment

default

default:使用哪一个环境,其值应为某个environment的id

id

id:environment的环境id

注:我们通过default值来选择使用的数据库(default的值和哪个environment的id值相同,使用的就是哪个库),因此需要保证default至少要匹配一个环境ID

事务管理器(transactionManager)

transactionManager:设置事务管理器

属性type:设置事务管理的方式

type="JDBC|MANAGED"

JDBC:表示使用JDBC中原生的事务管理方式

MANAGED:被管理,例如交给Spring来进行事务管理

注:一般不使用MyBatis来管理事务

数据库连接池(dataSource)

dataSource:配置数据库连接池和和相应的数据库连接信息

属性:

type:设置数据源类型

type="POOLED|UNPOOLED|JBDI"

POOLED:表示使用数据库连接池

UNPOOLED:表示不使用数据库连接池

JBDI:表示使用上下文中的数据源

注:一般也不使用MyBatis管理数据库连接池,因此只需要配置好数据库连接信息即可

driver:注册驱动("com.mysql.jdbc.Driver")

ur:连接路径

username:用户名

password:密码

引入properties文件

我们可以引入properties文件,之后就可以在当前文件中使用${key}的方式访问value

<properties resource="jdbc.properties" />

其中jdbc.properties在resources包下,其内部是许多以键值对形式存在的数据,之后就可以在核心配置文件中使用${键名}的方式访问到对应的值

设置(settings)

setting:设置全局配置

该标签只有一个子标签<setting>,子标签有两个属性:

name,value

name的值有许多,且有相对应value的值

mapUnderscoreToCamelCase

将下划线映射为驼峰

name="mapUnderscoreToCamelCase" value="true|false"

value默认为false,设置其为true后就可以自动将下划线映射为驼峰,即emp_id映射为empId

lazyLoadingEnabled,aggressiveLazyLoading

lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有的关联对象都会延迟加载,默认值为false

aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则每个属性会按需加载,默认值为false

这两个设置可以用于设置是否开启延迟加载,当lazyLoadingEnabled开启,aggressiveLazyLoading关闭时,延迟加载生效

类型别名(typeAliases)

typeAliases可以为Java类型设置一个别名,方便后续寻找

可以直接定位到某个类

如:

直接给类起别名的时候可以只写type,不写alias,则默认别名就是类名,且不区分大小写

如设置type为com.pojo.User,则默认别名就是user,不区分大小写

也可以指定一个包名

当指定包名,且无注解时,该包下所有类都会有默认的别名,如User的别名就是user,且不区分大小写(就算起了别名,也可以使用原名称)

映射器(mappers)

mappers:让MyBatis找到映射文件

有以下几种方式

一般使用最后一种方式,即Mapper代理开发中所说到的方式

但使用包的方式引入映射文件时,需要满足两个条件:

1.mapper接口和映射文件所在的包必须一致

2.mapper接口的名字和映射文件的名字必须一致

注意事项

在配置各个标签时,需要遵循前后顺序,如下

<?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="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <!--加载sql映射文件-->
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

SQL映射文件

标签介绍

namespace

namespace:命名空间,可以储存一系列的sql语句,可以理解为一个包,届时使用sql语句时可以通过命名空间和id来找到对应sql

select

select:查询

该标签有以下几个属性

id

id:sql语句的唯一标识,通过id可以找到对应sql,以便后续执行

parameterType

parameterType:用于设置参数类型,该参数可以忽略

resultType

resultType:结果类型,代表执行sql后返回的结果的数据类型,这里需要填写的类型应为对应类型的全类名,如com.pojo.User,但是在核心配置文件中设置别名后就可以简化为User了

注:这个结果类型可以是创建的pojo类,也可以是Integer,int等类型,且mybatis为许多类型都起了别名,如

int:_int、_integer

Integer:Integer、int

Map:map

String:string

且这些别名是不区分大小写的

返回map集合

当我们查询出来的数据没有对应的实体类时,我们可以返回一个map集合

届时查询出来的数据会以字段名为键,字段的值为值,封装到map集合中返回,且如果查询的字段中的数据为null,那么map集合中不会有相关的键值对。而查询到实体类时,即使字段数据为null,仍会有相关属性,且值为null

若我们查询出来的数据有多条时,并且要将每条数据转换为map集合时,可以用以下两种方式解决:
1.将mapper接口方法的返回值设置为泛是Map集合的List集合

2.将每条数据转换的map集合放在一个大的map中,但必须要使用@MapKey注解将查询的某个字段的值作为大的Map集合中的键

insert

insert:增添

该标签有以下几个属性

id

id:sql语句的唯一标识,通过id可以找到对应sql,以便后续执行

useGeneratedKeys

useGeneratedKeys:默认为false,设置为true后代表要返回主键

keyProperty

keyProperty:代表要将返回的主键传递给实体类的哪个属性(一般为id)

注:当配置了useGeneratedKeys和keyProperty两个属性,且从接口中传入的参数为一个对象时,在执行该sql语句后,会自动将增添的数据中的主键传给该对象中的某个属性(一般为id)

注:增删改返回的参数固定为受影响的行数(可以设置为void,即不处理返回的数据),因此想要获取自增的主键必须使用这两个属性

update

update:修改

该标签有以下几个属性

id

id:sql语句的唯一标识,通过id可以找到对应sql,以便后续执行

注:若接口中的返回值类型为int,则得到的是影响的行数

delete

delete:删除

该标签有以下几个属性

id

id:sql语句的唯一标识,通过id可以找到对应sql,以便后续执行

参数占位符

#{}

#{}:执行SQL时,会将#{}占位符替换为?,将来自动设置参数(无SQL注入问题)

${}

${}:拼SQL。会出现SQL注入问题。

使用时机

参数传递用#{}

若表名、列名不固定,只能用${}进行sql拼接

注:

当要传递多个参数时,方法中的多个参数需要用@Param()注解标记

@Param(xxx)类型 参数名

到时候这个参数就会传递给名称为xxx的参数占位符

例:下述@Param("status")int status接到的参数就会传递给#{status}

SQL语句中特殊字符处理

xml文件中的“<”等特殊字符可能会识别错误,出现问题

1.转义字符

使用xml文件自带的转义字符

2.<![CDATA[内容]]>

将符号放在以下内容中间,适用于出现大量特殊字符时

快捷键:idea中打出“CD”即可快速不全

注意事项

1.不同命名空间中id可以相同,在调用sql语句时有两种方式

全限定名:命名空间 + id

短名称:id

在id无重复时短名称可以正常使用,但当id在不同命名空间中重复出现时使用短名称会出错,因此一般使用全限定名

2.当sql语句中用到参数占位符时,对应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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

动态SQL

SQL语句会随着用户的输入或外部条件的变化而变化,我们称为动态SQL

if

if,类似于Java中的if,可以通过条件来判断是否执行sql语句

该标签只有一个属性,test

该属性代表条件语句,满足该条件则执行标签内的sql语句

如图

注:由于sql语句中where条件存在and等关键字,因此可能会出现语法错误(如if条件全部不满足),可以通过添加恒等式来解决(如上图),但一般更多使用where标签

where

我们可以使用where标签和if标签来解决多条件查询的问题

1.若where标签中有条件成立,会自动生成where关键字

2.会自动将where标签中前面多余的and去掉

3.若where标签中没有任何一个条件成立,则where没有任何功能

trim

trim可以更加灵活的去实现where的功能

trim,有四个属性

prefix、suffix:在标签中内容的前面或后面添加指定内容

prefixOverrides、suffixOverrides:在标签中内容的前面或后面去掉指定内容

trim、where、set

choose(when,otherwise)

当我们要实现从多个条件中选择一个时,可以使用choose标签

choose、when、otherwise,分别类似于Java中的if、else if、else

choose:类似于if

when:类似于else if,有test属性,代表条件,满足条件就拼接内部的SQL语句。至少一个

otherwise:类似于else,当所有when都未满足时,执行otherwise中的sql语句。最多一个

注:当有一个when的条件满足时,后面所有的when以及otherwise都不会再进行判断

注:上图中的otherwise是为了解决“用户没有传入任一条件”的情况下,sql语法的问题,实际上可以使用where标签解决

foreach

foreach:用于遍历数组或集合

该标签有以下几个属性

collection

collection:传入的数组或集合

注:当传入的是数组时,mybatis会将数组参数封装成一个Map集合,默认键名为array,值为数组

当传入的是List集合时,会自动封装成Map集合,默认键名为list

当传入的是数组或List集合时,collection的属性值需要为list或array,也可以在接口中使用@Param注解来改变map集合的键的名。当传入的是Map集合时,collection的属性值为该集合的名称

item

item:代表本次迭代得到的元素,在后面使用参数占位符时应使用相同的元素名(必选)

index

index:当传入的为除map以外的集合或者数组时,代表索引。当传入的为map集合时,代表键(可选)

separator

separator:代表每两次拼接中间的分隔符,且会自动在前后各加一个空格(可选)

open

open:在最开始拼接的字符串(可选)

close

close:在最后拼接的字符串(可选)

sql片段

可以记录一段sql,在需要用的地方使用include标签进行引用

注:sql片段标签应该在SQL映射文件中,即与select等标签同级

include标签则是可以在任何需要用到sql片段的地方均可使用

创建SqlSessionFactory代码优化

这串代码中,若有多个Servlet,那么代码将会重复多次,并且可能会创建多个SqlSessionFactory工厂,导致资源浪费,因此应只创建一次工厂

解决方法

代码重复:工具类

只创建一次工厂:静态代码块

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionFactoryUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            String resource = "mybatis-config.xml";//这里为核心配置文件的相对路径
            InputStream inputStream = Resources.getResourceAsStream(resource);//需要抛出异常
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getSqlSessionFactory(){
        return sqlSessionFactory;
    }
}

注意事项

1.字段名和属性名不同

在使用Mapper接口的代理对象来执行方法时,如果数据库表中的字段名与实体类的属性名不一样,会导致无法自动封装数据

解决方法

1.1起别名

在编写sql语句时,给名称不一样的字段起别名,让别名和实体类的属性名一样

1.2设置全局映射

在核心配置文件的settings标签中设置

name="mapUnderscoreToCamelCase" value="true"

可以将所有的下划线自动映射为驼峰

1.3resultMap

定义一个resultMap标签来映射字段名和属性名

1.3.1定义<resultMap>

resultMap包含id和type两个属性

id:名称,唯一标签

type:类型,要和哪一个类型做映射

resultMap的子标签有四个,这里只用到两个

id:处理主键字段和实体类属性的映射关系

result:处理普通字段的和实体类属性的关系映射

这两个标签都有两个属性

column:字段名

property:属性名

1.3.2使用resultMap属性替换resultType

在<select>标签中,使用resultMap来替换resultType

例:

2.多对一

mysql中我们经常会将两张表关联起来,如一对多等关系

在一对多的关系中,我们需要创建两个实体类,分别对应两张表,

且在一的实体类中,应有一个成员变量,其类型为另一个实体类

在多的实体类中,应有一个成员变量,其类型为 泛型是另一个实体类的集合

之后我们在查询时,如查询员工表和部门表,查询到的数据为员工的信息及其对应部门的信息,但是此时我们设置的返回类型只有一个,如员工实体类,那么对应部门的信息我们是无法封装的

我们可以使用resultMap来自定义映射的方式解决,且有三种方式:

2.1级联

在设置映射时,我们可以将mysql中部门的相关字段 与 员工实体类中的部门对象的属性进行映射,如下图

property中的dept是员工实体类中部门对象的名称

2.2 association

resultMap除id和result外还有两个子标签

association:处理多对一,一对一的映射关系(处理 类型为实体类 的属性)

其有两个属性

property:设置需要处理映射关系的属性的属性名

javaType:设置要处理的属性的类型

有两个子标签

id:处理主键字段和实体类属性的映射关系

result:处理普通字段的和实体类属性的关系映射

这两个标签都有两个属性

column:字段名

property:属性名

2.3分步查询

我们可以将一次性查询员工及其所属部门信息的过程分成多步

1.查询员工信息

2.根据员工的部门id查询对应的部门

该方式也需要用到resultMap的association标签,但是用到的属性不完全相同

association,需要用到以下三个属性

property:设置需要处理映射关系的属性的属性名(用法与上个方法相同)

select:设置分步查询的sql的唯一标识(即下一个调用的sql语句的全类名)

column:将查询出的某个字段作为分步查询的sql的条件(即下一个调用的sql语句的参数)

注:

1.分步查询在开始第一步,即调用了resultMap对应的sql语句后,可以自动调用下一个sql语句,即select对应的sql语句

2.column为查询出来的某个字段,这个字段在员工实体类中不一定存在

延迟加载

分步查询的优点之一:可以实现延迟加载

即当进行分步查询时,可以只进行某一步的查询,而不是直接查询到底

如我们只查询员工的信息,不查询对应部门,则可以开启延迟加载,节省资源

开启方式:在核心配置文件的settings标签中设置lazyLoadingEnabled开启,aggressiveLazyLoading关闭(默认关闭,即可以不设置),开启后默认所有分步查询都只会查询一步

在设置了全局延迟加载后,我们可以在association中fetchType属性设置是否开启延迟加载

fetchType="eager(立即加载)| lazy(延迟加载)"

3.一对多

多对一查询时是查询员工及其对应部门的信息

一对多查询时是查询部门及其员工

同多对一,我们也需要解决查询到的数据与实体类中属性的不对应的情况,有以下两种方式:

3.1collection

resultMap的最后一个子标签

collection:处理一对多,多对多的映射关系(处理集合类型的属性)

collection与association的用法类似,有属性property和ofType

property:需要进行映射的属性(和association相同)

ofType:设置集合类型的属性中存储的数据的类型(即泛型的类型)

同样有子标签id,result,内有属性column和property

column:字段名

property:属性名

3.2分步查询

同多对一的分步查询,可以将查询部门及部门员工分为两步:

1.查询部门信息

2.查询该部门的员工信息

需要用到resultMap的collection,具体用法与使用association进行分步查询一致

也可以设置延迟加载

4.事务

当我们进行select操作时,不需要进行事务的管理,但当我们进行添加,删除操作时,需要手动提交事务

openSession():默认开启事务,进行增删改操作后需要使用sqlSession.commit();手动提交事务

openSession(true):设置为自动提交事务(关闭事务)

注解开发

当功能比较简单的时候,可以直接使用注解来代替配置文件完成开发,会更加方便

如图,不需要配置SQL映射文件,直接在对应接口方法上方加入注解即可

注:注解开发仅适用于完成简单功能,复杂功能还是使用配置文件

特殊SQL的执行

1.模糊查询

当我们进行模糊查询的时候,如果使用#{}来直接传递参数时,mybatis在解析SQL语句时会将#{}变为?占位符,并且在之后将其代替,但是模糊查询时我们会使用单引号将其包裹,如下图

那么在解析时,#{}变为?占位符后会在单引号中,此时?占位符会被识别为一个字符串,而不是占位符,这会导致之后mybatis调用方法去代替占位符时找不到?而报错

解决方案:

1.使用${},直接拼接

2.使用mysql的concat函数,这个函数可以拼接字符串

如图,concat会将两个%和#{}的内容拼接到一起并返回对应字符串

3.为两个%加上双引号,中间包裹#{}

一般使用第三种

2.批量删除

我们经常会用到批量删除的功能,此功能一般需要传输多个值,然后根据这些值删除数据

实现该功能的SQL语句有以下两种:

当我们使用第一种时,由于需要的参数的数目固定,是可以直接使用#{},但更多的时候删除的数目是不固定的,也就是要使用第二种SQL语句,此时需要传递的数据应为一个id数组

如果我们直接使用#{},由于#{}在使用时会自动在两边加上单引号,即数据为7,8的数组最后会变为'7,8',这并不符合mysql中in函数的语法,会报错

解决方案:

1.使用${}直接拼接

2.使用foreach来代替,设置起始为左括号,结尾为右括号,分割为逗号,然后遍历传递的数组即可

3.动态设置表名

当我们需要动态的设置表名时,由于#{}会自动的加单引号,因此我们只能使用${}

4.添加功能获取自增的主键

我们在执行添加功能后可能需要获得对应自增的主键,那么就需要在设置insert语句时设置对应的属性,即

useGeneratedKeys:默认为false,设置为true后代表要返回主键

keyProperty:代表要将返回的主键传递给实体类的哪个属性(一般为id)

通过这两个属性即可在调用增加方法后为对应的实体类的id属性赋值

Mybatis的缓存

一级缓存

一级缓存是sqlSession级别的,通过同一个sqlSession查询的数据会被缓存,下次执行相同的查询语句,就会从缓存中直接获取,不会从数据库重新访问。一级缓存是默认开启的

一级缓存失效的三种情况:

1.不同的sqlSession对应不同的一级缓存

2.同一个sqlSession两次查询期间进行了任何一次增删改操作(会自动清空该sqlSession的缓存)

3.同一个sqlSession两次查询期间手动清空了缓存

手动清空一级缓存:sqlSession.clearCache()

二级缓存

一级缓存是sqlSessionFactory级别的,通过同一个sqlSessionFactory创建的sqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

二级缓存开启的条件:

1.在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置

2.在sql映射文件中设置标签<cache/>

3.二级缓存必须在SqlSession关闭或提交之后有效

4.查询的数据所转换的实体类类型必须实现序列化的接口

使二级缓存失效的情况:两次查询之间执行任意的增删改操作,会使一级缓存和二级缓存同时失效

二级缓存的相关配置

注:这些配置一般使用默认的,不需要进行修改

在mapper配置文件中添加的cache标签可以设置一些属性:

1.eviction属性:缓存回收策略,默认的是 LRU。

LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。

FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。 SOFT – 软引用:移除基于垃 圾回收器状态和软引用规则的对象。

WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

2.flushInterval属性:刷新间隔,单位毫秒,默认情况是不设置,也就是没有刷新间隔,缓存仅仅执行增删改操作时刷新

3.size属性:引用数目,正整数代表缓存最多可以存储多少个对象,太大容易导致内存溢出

4.readOnly属性:只读, true/false

true:只读缓存;会给所有调用者返回缓存对象的相同实例(即直接返回缓存的对象)。因此这些对象不能被修改。这提供了很重要的性能优势。

false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

MyBatis缓存查询的顺序

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。

如果二级缓存没有命中,再查询一级缓存

如果一级缓存也没有命中,则查询数据库

SqlSession关闭之后,一级缓存中的数据会写入二级缓存

整合第三方缓存EHCache

我们可以将二级缓存改为整合的第三方缓存EHCache

Mybatis的逆向工程

正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。

逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:

  • Java实体类
  • Mapper接口
  • Mapper映射文件

注:通过逆向工程实现的实体类中不会包含其他实体类对象,即只能实现单表的实体类

创建逆向工程的步骤

1.添加依赖和插件

  <dependencies>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.7</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- log4j日志 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>
  </dependencies>



  <!-- 控制Maven在构建过程中相关配置 -->
  <build>
  <!-- 构建过程中用到的插件 -->
  <plugins>
    <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
    <plugin>
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-maven-plugin</artifactId>
      <version>1.3.0</version>
      <!-- 插件的依赖 -->
      <dependencies>
        <!-- 逆向工程的核心依赖 -->
        <dependency>
          <groupId>org.mybatis.generator</groupId>
          <artifactId>mybatis-generator-core</artifactId>
          <version>1.3.2</version>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.33</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
  </build>

2.创建MyBatis的核心配置文件

3.创建逆向工程的配置文件

注:在resources包下创建逆向工程的配置文件,文件名必须是 generatorConfig.xml

<?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: 生成基本的CRUD(清新简洁版)
    MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/db1?serverTimezone=UTC"
                        userId="root"
                        password="1234">
        </jdbcConnection>
        <!-- javaBean(实体类)的生成策略-->
        <javaModelGenerator targetPackage="com.mybatis.pojo" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.mybatis.mapper" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.mybatis.mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="emp" domainObjectName="Emp"/>
        <table tableName="dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>

4.执行MBG插件的generate目标

QBC查询

我们在创建逆向工程时,可以通过设置generatorConfig.xml文件中context标签的targetRuntime属性来选择是生成基本的增删改查还是带条件的增删改查

基本的增删改查较为简单,且只有五条sql语句

带条件的增删改查功能很全面,mapper中包含许多方法,其中

insert:根据条件添加,若添加的内容中包含null,则对应字段的值也为null

insertSelective:根据条件选择性添加,若添加的内容中包含null,则不会对 对应字段赋值,即会使用musql中 的默认值,若为该字段的默认值设置了值,则结果与insert方法不同

updateByExample:根据条件修改,若修改的内容中包含null,则对应字段会设置为null

updateByExampleSelective:根据条件选择性修改,若修改的内容中包含null,则不会修改对应字段

Exmaple

mapper中很多需要条件的方法接收的参数为xxxExample类型,即若实体类为Emp,则对应条件类型为EmpExample

若直接传入一个null,代表没有条件

使用example时,我们首先需要调用方法createCriterid()创建条件查询,

之后链式调用and + 属性名 + 条件 方法即可

and:代表多个条件之间以and连接,因此可以直接链式编程

属性名:代表我们要对哪一个属性进行条件限制,如Id代表要对Id进行条件限制

条件:代表要进行什么条件限制,如Like代表模糊匹配,EqualTo代表要求属性名与什么相同

例:example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);

该条件对应的是:EmpName要与"张三"相同,且Age要大于等于20

之后将对应的example作为参数提供给方法即可

若需要使用or来连接条件,则需要调用or()方法,然后调用andxxx即可

例:example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);

example.or().andDidIsNotNull();

该条件中Did不是null的条件就会和前面的条件以or连接

@Test
public void testMBG(){
    try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new
        SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        
        //查询所有数据
        /*List<Emp> list = mapper.selectByExample(null);
        list.forEach(emp -> System.out.println(emp));*/
        
        //根据条件查询
        /*EmpExample example = new EmpExample();
        example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);
        example.or().andDidIsNotNull();
        List<Emp> list = mapper.selectByExample(example);
        list.forEach(emp -> System.out.println(emp));*/
        
        mapper.updateByPrimaryKeySelective(new Emp(1,"admin",22,null,"456@qq.com",3));
     
    } catch (IOException e) {
        e.printStackTrace();
    }
}

分页插件

分页是一个十分常用的功能,且在实现分页功能时通常需要进行许多的逻辑判断等操作,为简化这些操作,可以使用分页插件

使用步骤

1.添加依赖

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

2.配置分页插件

在Mybatis的核心配置文件中配置插件

<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

在配置完分页插件后,只要我们开启分页功能,那么只要我们发送查询请求后,分页插件就会自动拦截请求,并且为我们的查询语句加上limit(分页)语句

分页插件的使用

在进行查询功能之前需要开启分页功能:

PageHelper.startPage(int pageNum, int pageSize)

pageNum:当前页的页码

pageSize:每页显示的条数

在查询获取list集合后,可以获取分页相关数据:

PageInfo pageInfo = new PageInfo<>(List list, int navigatePages)

list:分页之后的数据

navigatePages:导航分页的页码数,即显示在页面上的页码,如下图

这里显示了5个页码,即navigatePages为5

所得的pageInfo中就包含许多处理好的分页相关的数据,如下:

PageInfo{ pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8, list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30, pages=8, reasonable=false, pageSizeZero=false}, prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8, navigatepageNums=[4, 5, 6, 7, 8] }

pageNum:当前页的页码

pageSize:每页显示的条数

size:当前页显示的真实条数

startRow:

endRow:

total:总记录数

pages:总页数

prePage:上一页的页码

nextPage:下一页的页码

isFirstPage/isLastPage:是否为第一页/最后一页

hasPreviousPage/hasNextPage:是否存在上一页/下一页

navigatePages:导航分页的页码数

navigatepageNums:导航分页的页码,[1,2,3,4,5]

其内部有一个list,值为Page对象的数据,Page对象里面包含部分分页相关数据,且该对象可以在开启分页功能时获得

        //获取mapper
        String resource = "mybatis-config.xml";//这里为核心配置文件的相对路径
        InputStream inputStream = Resources.getResourceAsStream(resource);//需要抛出异常
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

        //开启分页功能(可以在此处获取Page对象)
        Page<Object> page = PageHelper.startPage(1,4);
        //调用查询功能
        List<Emp> emps = mapper.selectAll();
        //获取分页相关数据
        PageInfo pageInfo = new PageInfo(emps,4);

        System.out.println(pageInfo);
  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值