MyBatis入门-配置文件和映射文件
学习目标
- 能够了解什么是框架
- 掌握mybatis框架开发快速入门
- 掌握mybatis的dao层动态代理实现
- mybatis核心配置
- 可以写出会话工具类
-
今日作业
1.搭建maven工程,运行hello world
2.搭建mybatis环境,查询所有用户,使用代理方式
3.掌握核心配置文件的常用四个标签 properties settings typealiases mappers
4.预习(1-3个小时)1.框架概述【了解】
目标
什么是框架
- 框架解决了哪些问题
- 分层开发下常见的框架
什么是框架
程序开发中的框架往往是对常见功能的封装,通常与具体业务无关,也可以认为是软件的半成品。程序框架理解为基础或者机械标准件(例如螺丝螺母标准的机械部件)。
假如你要造一辆马车,在没有框架的情况下,你需要自己去伐木,去把木头做成木板,木棍,然后组成轮子,门,等部件,然后组装起来。但如果你用了框架,就相当于你有现成的轮子,门等部件,你只需要组装一下就可以了。
一个框架是一组可复用的设计构件。
框架(Framework)是整个或者部分系统的可重用设计,是JavaEE底层技术的封装。
框架是可以被开发者定制的应用骨架。
框架是一个半成品,软件是成品。我们在它的基础上开发出成品(软件)。
框架解决的问题
1 解决了技术通用的问题
在JavaEE体系中,有着各种各样的技术。不同的软件企业,根据自身的业务需求选择不同的技术,容易造成应用依赖技术,增加了项目开发实现的复杂性和技术风险性。而框架技术就可以解决上述问题。
2 提升了开发效率
企业项目中使用框架,只需要专注实现业务需求。使用框架的方便性,提升了开发效率。
3 提升了系统稳定性
一个成熟的框架,经过了在众多企业项目中的验证使用,稳定性有保障。
小结
- 理解为软件中标准件,程序员可以利用这些半成品来开发软件。
- 提高开发效率,降低开发成本。
- 以后我们在企业中开发项目基本上都是使用框架来开发
2.mybatis框架介绍【了解】
目标
- mybatis框架介绍
-
mybatis框架介绍
mybatis是Apache软件基金会下的一个开源项目,前身是iBatis框架。2010年这个项目由apache 软件基金会迁移到google code下,改名为mybatis。2013年11月又迁移到了github(GitHub 是一个面向开源及私有 软件项目的托管平台)。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射(多表)。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。它对 jdbc 的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建 connection、创建 statement、手动设置参数、结果集检索等 jdbc 繁杂的过程代码。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。mybatis的优点
简单易学:mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个SQL映射文件即可。
- 使用灵活:Mybatis不会对应用程序或者数据库的现有设计强加任何影响。SQL语句写在XML里,便于统一管理和优化。
解除SQL与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易进行单元测试。SQL语句和代码的分离,提高了可维护性。
mybatis的不足
编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
- SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
- 框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
官方网站
官网地址:http://www.mybatis.org/mybatis-3/
源码和包下载地址:https://github.com/mybatis/mybatis-3/releases
mybatis框架整体架构
1、配置文件
全局配置文件(核心配置文件):mybatis-config.xml,作用:配置数据源(配置数据库连接信息),引入映射文件
映射文件:XxMapper.xml,作用:配置sql语句、参数、结果集封装类型等
2、SqlSessionFactory
作用:获取SqlSession
通过new SqlSessionFactoryBuilder().build(inputStream)来构建,inputStream:读取配置文件的IO流
3、SqlSession
作用:执行CRUD操作
4、Executor
执行器,SqlSession通过调用它来完成具体的CRUD
5、StatementHandler
在映射文件里面配置,包含3部分内容:
具体的sql,sql执行所需的参数类型,sql执行结果的封装类型
参数类型和结果集封装类型包括3种:
HashMap,基本数据类型,pojo
MyBatis的ORM方式
Object Relational Mapping 对象关系映射
MyBatis的两种映射方式:
- 通过XML映射
-
小结
1、mybatis解决了三层(web,service,dao)中哪一层的问题?
dao(数据持久层)
2、mybatis框架是对什么技术进行的封装?
JDBC
3、mybatis框架中的核心组件有哪些?
SqlSessionFactoryBuilder:SqlSessionFactory工厂的创造类
SqlSessionFactory:加载配置信息,提供SqlSession
SqlSession: 数据操作的对象 sql语句
3.MyBatis框架入门开发【掌握】
3.1入门案例:环境的搭建和代码实现
目标
需求
准备数据
选定一个数据,在里面创建表插入数据。
要查询的表create table user (
id int primary key auto_increment,
username varchar(20) not null,
birthday date,
sex char(1) default '男',
address varchar(50)
);
insert into user values (null, '孙悟空','1980-10-24','男','花果山水帘洞');
insert into user values (null, '白骨精','1992-11-12','女','白虎岭白骨洞');
insert into user values (null, '猪八戒','1983-05-20','男','福临山云栈洞');
insert into user values (null, '蜘蛛精','1995-03-22','女','盤丝洞');
select * from user;
步骤
我们完全可以按照mybatis官网入门步骤进行开发即可:
mybatis – MyBatis 3 | 入门 创建maven工程,导入坐标
我们以前要操心项目中要用到第三方jar包引入,现在一切交给maven就行了。
将下面依赖拷贝到 pom.xml 中
<!--mybatis核心包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.0</version>
</dependency>
<!--logback日志包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
- 在resources下面创建核心配置文件:mybatis-config.xml
XML 配置文件中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。 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>
<!--mybatis环境的配置-->
<environments default="development">
<!--通常我们只需要配置一个就可以了, id是环境的名字 -->
<environment id="development">
<!--事务管理器:由JDBC来管理-->
<transactionManager type="JDBC"/>
<!--数据源的配置:mybatis自带的连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db4"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载映射文件,放到resources下即可-->
<mapper resource="userMapper.xml"/>
</mappers>
</configuration>
- 在resources下创建映射文件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"> <!-- namespace命名空间,设置为对应Mapper接口全路径名一样 --> <mapper namespace="com.itheima.sh.dao.UserMapper"> <!-- 查询语句 resultType:返回的实体类的类型,类全名 --> <select id="findAllUsers" resultType="com.itheima.sh.pojo.User"> select * from user </select> </mapper>
4.在resources下 导入logback.xml文件
文件:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--输出流对象 默认 System.out 改为 System.err-->
<target>System.out</target>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
</encoder>
</appender>
<!-- File是输出的方向通向文件的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--日志输出路径-->
<file>C:/code/itheima-data.log</file>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式-->
<fileNamePattern>C:/code/itheima-data2-%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="ALL">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</root>
</configuration>
4.编码
新建UserMapper接口:
public interface UserMapper { /** 查询所有的用户 */ List<User> findAllUsers(); }
将接口和映射文件绑定:
映射文件的 namespace值==接口的全路径 映射文件的sql语句的 id值 ==接口中的方法名
创建实体类User
package com.itheima.sh.pojo; import java.sql.Date; public class User { private Integer id; private String username; private Date birthday; private String sex; private String address; public User() { } public User(Integer id, String username, Date birthday, String sex, String address) { this.id = id; this.username = username; this.birthday = birthday; this.sex = sex; this.address = address; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", birthday=" + birthday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } }
编写测试类代码
编写流程:Resources工具类直接可以读取src目录下配置文件,转成输入流。
public class MyBatisTest01 {
//从MySQL中查询所有的用户
@Test
public void test02() throws Exception{
//1.从xml文件中构建SqlSessionFactory
//定义核心配置文件路径
String resource = "mybatis-config.xml";
//加载核心配置文件获取输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.从SqlSessionFactory中获取session
SqlSession session = sqlSessionFactory.openSession();
//3.使用session获取接口的动态代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
//4.使用接口对象调用接口中的方法
List<User> userList = mapper.findAllUsers();
//5.遍历集合
for (User user : userList) {
System.out.println(user);
}
//关闭会话
session.close();
}
}
三大对象作用和生命周期
在mybatis中一个会话相当于一次访问数据库的过程,一个会话对象类似于一个Connection连接对象。
- SqlSessionFactoryBuilder:这是一个临时对象,用完就不需要了。通过这个工厂建造类来创建一个会话工厂。
- SqlSessionFactory:从一个工厂类中得到一个会话对象,一个项目中只需要创建一个会话工厂对象即可。通过会话工厂对象来创建会话对象。
- SqlSession: 每次访问数据库都需要创建一个会话对象,这个会话对象不能共享。访问完成以后会话需要关闭。
- 运行结果:
注意
mybatis配置文件分两种
- 核心配置文件:mybatis-config.xml 配置连接数据库参数
- 映射文件:UserMapper.xml编写SQL语句
项目结构
小结
mybatis动态代理对象实现过程:
【1】书写dao层: UserMapper.java接口 --定义方法
【2】把接口和映射文件绑定:
1、映射文件的namespace="接口的全限定类名";
2、映射文件中sql语句的id值必须为 接口中方法名
【3】获取接口的动态代理对象:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = userMapper.findAllUser();
3.2入门案例:mybatis执行流程分析
- 第一步:是从核心配置文件mybatis-config.xml中构建SqlSessionFactory对象,由于核心配置文件mybatis-config.xml中关联了映射文件UserMapper.xml,所以在SqlSessionFactory中也存在映射文件的内容
- 第二步:是从SqlSessionFactory中获取SqlSession会话对象,其实SqlSession会话对象底层封装的就是conn连接对象
- 第三步:是通过SqlSession会话对象调用查询方法selectList然后根据参数找到映射文件中中的sql语句并将数据封装到pojo的User对象中
3.3入门案例:核心配置文件 mybatis-config.xml
在resources目录下核心配置文件
- 可以在mybatis官网入门中找到配置模板:
https://mybatis.org/mybatis-3/zh/getting-started.html
<?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>
<!--mybatis环境的配置-->
<environments default="development">
<!--通常我们只需要配置一个就可以了, id是环境的名字 -->
<environment id="development">
<!--事务管理器:由JDBC来管理-->
<transactionManager type="JDBC"/>
<!--数据源的配置:mybatis自带的连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db4"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载映射文件,放到src下即可-->
<mapper resource="userMapper.xml"/>
</mappers>
</configuration>
- 可以在idea中定义模板,以后重用。
<?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> <!--加载外部的配置文件--> <properties resource="db.properties"></properties> <!--settings--> <settings> <!--开启驼峰自动映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--别名--> <typeAliases> <package name="com.itheima.sh.pojo"></package> </typeAliases> <!--mybatis环境的配置 一个核心配置文件,可以配置多个运行环境,default默认使用哪个运行环境 --> <environments default="development"> <!--通常我们只需要配置一个就可以了, id是环境的名字 --> <environment id="development"> <!--事务管理器:由JDBC来管理--> <!-- 事务管理器type的取值: 1. JDBC:由JDBC进行事务的管理 2. MANAGED:事务由容器来管理,后期学习Spring框架的时候,所有的事务由容器管理 --> <transactionManager type="JDBC"/> <!--数据源的配置:mybatis自带的连接池--> <!-- 数据源: 1. POOLED:使用mybatis创建的连接池 2. UNPOOLED:不使用连接池,每次自己创建连接 3. JNDI:由服务器提供连接池的资源,我们通过JNDI指定的名字去访问服务器中资源。 --> <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> <!--映射器--> <mappers> <!--加载其它的映射文件 注:注解开发是点号--> <!-- <package name="com.itheima.sh.dao"></package>--> <!--加载其它的映射文件 注:不是点号--> <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>--> <!-- 加载其它的映射文件 xml形式 包扫描方式加载mapper映射文件,说明: 1. 要求mapper映射文件,与mapper接口要放在同一个目录 2. 要求mapper映射文件的名称,与mapper接口的名称要一致 --> <package name="com.itheima.sh.dao"></package> </mappers> </configuration>
3.4入门案例:常见错误
- 在映射文件userManager.xml中的resultType位置没有书写类的全名
如果出现maven jdk版本问题
pom.xml指定编译插件的jdk
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!-- put your configurations here -->
<!--源码-->
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
3.5 MyBatisX 插件安装
打开IDEA的Plugins,在Marketplace 中搜索 MyBatisx 插件,安装,安装后需要重启。
该插件可以方便我们跟踪管理接口和xml映射文件的关系。
小结:
1、mybatis框架的核心组件:
SqlSessionFactoryBuilder;会话工厂构造类创建会话工厂对象
SqlSessionFactory:会话工厂类创建会话对象
SqlSession:会话类
2、入门程序开发流程:
【1】从xml文件中构建:SqlSessionFactory
【2】从SqlSessionFactory中获取:SqlSession
【3】通过SqlSession执行_映射文件文件中的sql语句
5.mybatis核心配置
mybatis-config.xml,是MyBatis的全局配置文件,包含全局配置信息,如数据库连接参数、插件等。整个框架中只需要一个即可。
mybatis全局配置文件中的配置顺序:注意如果配置多项,必须按照以下顺序进行配置
- properties:属性配置
- settings:设置
- typeAliases:类型别名设置
- typeHandlers:类型处理器
- enviroments:环境配置
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源) - mappers:映射器
5.1、properties(属性)
【作用】
1、可以加载外部的配置文件信息(比如properties文件)到mybatis-config.xml中
2、也可以通过子标签property设置属性;
【使用方式】
使用${key}获取设置的属性值;
加载外部properties使用示例
第一步:新建资源文件
新建jdbc.properties文件,将连接参数抽取到配置文件中:
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/students
jdbc.driver=com.mysql.jdbc.Driver
第二步:引入资源文件
在mybatis-config.xml文件中加载jdbc.properties文件;通过properties的属性resource引入;
【注意事项】:子标签和属性resource同时加载一个变量,先加载子标签,然后在加载resource关联的外部文件的值,那么子标签中的内容会被覆盖。此时子标签存在就没有意义了。
5.2、settings(设置)
settings参数有很多,今天咱们先学习驼峰匹配mapUnderscoreToCamelCase。翻译过来就是映射下划线到驼峰式命名。
【作用】
开启驼峰匹配:完成经典的数据库命名到java属性的映射。
- 经典数据库命名:如果多个单词之间,通常使用下划线进行连接。
- java驼峰命名:第二个单词首字母大写。
驼峰匹配:相当于去掉数据库的数据中的名字的下划线,和java进行匹配
数据库名字段名:hello_world 映射Java属性名:helloWorld
【应用场景】
从数据库中查到数据的字段名user_name和实体类属性名userName,不一致,导致查到的数据无法封装到实体类中。但是,实体类中的属性userName符合驼峰命名,数据库字段名user_name符合数据库经典字段命名。
【1】配置驼峰映射
在mybatis-config.xml中做如下配置:
<!--
2、settings设置:
mapUnderscoreToCamelCase :驼峰自动映射配置 满足条件:数据库字段名 user_name ,实体类属性名 userName
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
运行测试,查看结果:<br />![](https://cdn.nlark.com/yuque/0/2022/png/22810121/1651586081394-62fa0e54-3ce9-4eae-a93b-97b7d0a8a9b7.png#crop=0&crop=0&crop=1&crop=1&id=JtBKM&originHeight=27&originWidth=744&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)<br />【小结】
1、开启驼峰自动映射的作用:user_name ====> userName;
2、注意事项:字段名必须符合user_name格式,属性名必须符合驼峰命名规则;
【2】解决字段名和属性名不一致的方法
如果数据库字段名和属性名不一致,或者也不满足上面的命名规则。可以通过在SQL语句中为字段名取别名进行映射。
5.3、typeAliases(类型别名)
【1】作用
类型别名是给类的全限定名称(包名.类名) 取一个短名称。存在的意义仅在于用来减少类全限定名的冗余。例如:
这些全限定类名可通过设置类型别名—短名称代替。类型别名的设置有以下几种方式:
方式:使用typeAliases标签的子标签package包扫描映射别名;
【2】应用
1、单独取别名:单独指定某一个类型的别名
2、批量取别名:扫描指定包下的所有类,扫描之后的别名就是类名,大小写不敏感(不区分大小写),建议使用的时候和类名一致。
设置别名:
<configuration>
....
<typeAliases>
<!--单独取别名-->
<typeAlias type="com.itheima.sh.pojo.User" alias="User"/>
<!--批量取别名-->
<package name="com.itheima.sh.pojo"/>
</typeAliases>
...
</configuration>
【内置别名】
这是一些为常见的 Java 类型内建的相应的类型别名。它们都是不区分大小写的,注意对基本类型名称重复采取的特殊命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
【代码演示】
<!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名-->
<select id="queryById" resultType="user" parameterType="int">
select * from user where id = #{id}
</select>
5.4、typeHandlers(类型处理器)【了解】
实现数据库数据类型和Java类型之间的转换。
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。
5.5、environments【了解】
MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;
尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离。
父标签: environments(环境配置)
子标签:
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
<!--mybatis环境的配置-->
<environments default="development">
<!--通常我们只需要配置一个就可以了, id是环境的名字 -->
<environment id="development">
<!--事务管理器:由JDBC来管理-->
<transactionManager type="JDBC"/>
<!--数据源的配置:mybatis自带的连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db4"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="test">
....
</environment>
</environments>
【1】默认环境设置
第一步:在environments标签中配置多个environment,通过属性default指定一个默认环境配置;
第二步:在构建SqlSessionFactory时,可指定具体环境,如果不指定就使用默认的环境配置;
【2】指定环境设置
第一步:在environments中配置多个环境
第二步:在构建SqlSessionFactory时,通过environment的id指定环境
代码演示:
<!--mybatis环境的配置-->
<!--<environments default="test">-->
<environments default="development">
<!--通常我们只需要配置一个就可以了, id是环境的名字 -->
<environment id="development">
<!--事务管理器:由JDBC来管理-->
<transactionManager type="JDBC"/>
<!--数据源的配置:mybatis自带的连接池-->
<dataSource type="POOLED">
<!--
这里的value="${driver}" driver 是子标签property的name属性值
-->
<property name="driver" value="${driver}"/>
<property name="url" value="jdbc:mysql://localhost:3306/db4"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
<!--通常我们只需要配置一个就可以了, id是环境的名字 -->
<environment id="test">
<!--事务管理器:由JDBC来管理-->
<transactionManager type="JDBC"/>
<!--数据源的配置:mybatis自带的连接池-->
<dataSource type="POOLED">
<!--
这里的value="${driver}" driver 是子标签property的name属性值
-->
<property name="driver" value="${driver}"/>
<property name="url" value="jdbc:mysql://localhost:3306/db4"/>
<property name="username" value="root"/>
<property name="password" value="12345"/>
</dataSource>
</environment>
</environments>
@Test
public void queryById( ) throws Exception {
//获取mapper接口的动态代理实现
//1、从xml中构建SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//这里指定了环境为test
//SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"test");
//build不方法不指定环境就使用默认的 <environments default="development">
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//3、获取UserMapper接口的动态代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryById(1);
System.out.println("user = " + user);
}
【注意事项】
虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用第三方的连接池:druid,C3P0。并且使用spring来管理数据源(连接池),来做到环境的分离(后续学到习Spring框架时会学到)。
5.6、mappers(映射器)
作用:关联加载映射文件;
方式一:关联映射文件 xml文件-mapper
在mybatis-config.xml文件中,通过mapper标签的resource属性引入当前工程resource下的映射文件。
<mappers>
<!--加载映射文件,放到resource下即可-->
<mapper resource="UserMapper.xml"/>
</mappers>
方式二:关联映射接口所在包-package
在mybatis-config.xml中,开启包扫描:扫描包下的所有接口和对应的映射文件。但必须满足以下条件:
1、接口名和映射文件名保持一致;
2、路径保持一致;
在resources目录中中创建多级目录 com.itheima.sh.dao 时按照如下输入
会创建一系列目录:com , itheima, sh, dao
错误的演示:
会创建一个目录: com.itheima.sh.dao
在mybatis-config.xml配置mapper接口的全路径:
<mappers>
<package name="com.itheima.sh.dao"/>
</mappers>
【原理】:扫描目标包目录下的mapper接口,并按照class的方式找到接口对应的映射文件。
5.7、全局配置小结
一、全局配置:如果需要配置多个,必须按照以下顺序
properties(属性)重点
settings(设置)重点
typeAliases(类型别名) 重点
typeHandlers(类型处理器)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
mappers(映射器)重点
二、常用配置:
1、properties(属性)
【作用】定义一些属性或者加载外部的资源文件
【用法】
2、加载外部的资源文件(src, jdbc.properties)掌握
<properties resource="jdbc.properties"></properties>
获取参数: ${driver}
2、settings(设置):
驼峰自动映射:数据库下划线命名:user_name ===>实体类属性命名:userName
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
说明:如果不满足驼峰式命名,查询时起别名。
3、typeAliases(类型别名)
【作用】给类的全名称 取 短名称 com.heima.pojo.User===>User,不区分大小写
【配置】
<typeAliases>
<package name="com.heima.mybatis.pojo"/> 掌握
</typeAliases>
4、mappers(映射器)
【作用】加载映射器
【配置】
关联映射文件 xml文件-mapper
<mappers>
<!--加载映射文件,放到src下即可-->
<mapper resource="UserMapper.xml"/>
</mappers>
关联映射接口所在包-package
<mappers>
<package name="com.itheima.sh.dao"/>
</mappers>
6.mybatis环境搭建
1、需求
搭建mybatis环境,完成dao层根据id查询用户数据功能;
create table user (
id int primary key auto_increment,
username varchar(20) not null,
age int,
birthday date,
sex char(1) default '男',
address varchar(50)
);
insert into user values (null, '孙悟空',30,'1980-10-24','男','花果山水帘洞');
insert into user values (null, '白骨精',20,'1992-11-12','女','白虎岭白骨洞');
insert into user values (null, '猪八戒',20,'1983-05-20','男','福临山云栈洞');
insert into user values (null, '蜘蛛精',30,'1995-03-22','女','盤丝洞');
select * from user;
2、环境搭建
1、创建项目,导入坐标
2、准备接口和实体类:UserMapper.java,User.java
【1】创建maven工程,导入坐标
- 日志记录包:mybatis在执行过程中输出的一些信息,mybatis底层需要使用logback日志框架
- mysql驱动
mybatis框架的包
<!--mybatis核心包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.0</version> </dependency> <!--logback日志包--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.18</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency>
【2】准备实体类和接口:User.java,UserMapper.java
User.javapublic class User { private Integer id; private String username; private Integer age; private Date birthday; private String sex; private String address; ...... }
UserMapper.java
public interface UserMapper { //根据id查询 User queryById(Integer id); }
3、mybatis配置
1、配置mybatis全局配置:mybatis-config.xml 2、配置mybatis映射文件:UserMapper.xml 3、测试
【1】配置mybatis全局配置: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="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///数据库名称"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
【2】配置mybatis映射文件: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="com.heima.mybatis.mapper.UserMapper"> <!--根据id查询--> <select id="queryById" resultType="com.heima.mybatis.pojo.User"> select * from user where id = #{id} </select> </mapper>
【3】测试
public class MyBatisTest { @Test public void queryById( ) throws Exception { //获取mapper接口的动态代理实现 //1、从xml中构建SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2、获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //3、获取UserMapper接口的动态代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.queryById(1); System.out.println("user = " + user); } }
7.mybatis映射文件配置
Mapper映射文件中定义了操作数据库的sql,每一个sql都被包含在一个statement中。映射文件是mybatis操作数据库的核心。
在映射文件中我们需要研究的内容有:SQL语句,入参,返回值
7.1、SQL语句-CRUD
映射文件中需要直接书写SQL语句对数据库进行操作,对数据库操作SQL语句主要有CRUD这四类。这四类对应到映射文件中的配置为四类标签:`select`,`insert`,`update`,`delete` 。
- 接口
public interface UserMapper { /* 查询所有用户 */ List<User> findAllUsers(); //根据id查询 User queryById(Integer id); //添加 void saveUser(User user); //修改 void updateUser(User user); //根据id删除 void deleteUser(Integer id); }
【1】select
Select标签:用来编写查询语句的statement【语法】
UserMapper.xml代码:<!--根据id查询用户数据--> <!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名--> <select id="queryById" resultType="user" parameterType="int"> select * from user where id = #{id} </select>
【属性】
| 属性名 | 说明 | 是否必须 | | —- | —- | —- | | id | 这条SQL语句的唯一标识,和接口的方法名一致 | 是 | | parameterType | 入参类型 | 否 | | resultType | 返回值类型 | 是 |
【java代码】
public class MyBatisTest01 {
private static UserMapper userMapper;
@BeforeClass
public static void berofeClass() throws Exception {
//1、从xml中构建SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
//这里指定了环境为test
// SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(in,"test");
//build不方法不指定环境就使用默认的 <environments default="development">
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build(in);
//2、获取SqlSession
SqlSession sqlSession = sf.openSession(true);//自动事务
//3、获取UserMapper接口的动态代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void queryById() throws Exception {
//获取mapper接口的动态代理实现
User user = userMapper.queryById(1);
System.out.println("user = " + user);
}
}
说明: userMapper只需要创建一个对象即可,所以在这里需要将获取userMapper的对象代码放到@BeforeClass注解修饰的方法中。下面直接使用同一个userMapper对象即可。
【2】insert
insert标签:编写新增语句的statement
int saveUser(User user);
【语法】
<insert id="saveUser">
insert into user values (null ,#{username},#{birthday},#{sex},#{address})
</insert>
注意:
{username} , #{birthday} , #{sex} , #{address} 大括号里面的值必须和pojo实体类User类中的属性名一致,否则会报错。
其实这里看的是User类中的getXxx()的get后面的xxx内容。例如 getUserName—-看的是userName ,就是将get去掉,U变为小写u,后面不变
【属性】
属性 | 说明 | 是否必须 |
---|---|---|
id | 这条SQL语句的唯一标识,和接口的方法名一致 | 是 |
【测试】
注意:需要改为自动提交事务。
SqlSession sqlSession = sqlSessionFactory.openSession(true);
测试新增方法
@Test
public void saveUser() throws Exception {
User user = new User();
user.setUsername("红孩儿");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("火焰山");
userMapper.saveUser(user);
}
说明:
mysql是自动提交事务,但是如果mysql将事务交给mybatis处理,而mybatis属于手动处理事务,需要使用SqlSession sqlSession = sqlSessionFactory.openSession(true);
来开启自动提交事务
【3】update
update标签:编写更新语句的statement
int updateUser(User user);
//返回值表示更新的行数
【语法】
<!--修改-->
<!--
username = #{username}
1.等号左边表示数据表列名
2.#{username} 这里的username需要和pojo中的get和set方法后面的标识符一致
-->
<update id="updateUser">
update user set username = #{username},birthday=#{birthday},sex=#{sex}, address = #{address} where id = #{id}
</update>
【属性】
属性 | 说明 | 是否必须 |
---|---|---|
id | SQL语句的唯一标识,和接口的方法名一致 | 是 |
【测试】
@Test
public void updateUser(){
User user = new User();
user.setUsername("铁扇公主");
user.setBirthday(new Date());
user.setSex("女");
user.setAddress("翠云山芭蕉洞");
user.setId(6);
userMapper.updateUser(user);
}
【注意事项】
使用SQL语句修改数据时需要根据id修改,所以id必须传入;
【4】delete
int deleteUser(Integer id);
【语法】
<!--删除-->
<delete id="deleteUser" >
delete from user where id = #{id}
</delete>
【属性】
属性 | 说明 | 是否必须 |
---|---|---|
id | SQL语句的唯一标识,和接口的方法名一致 | 是 |
【测试】
@Test
public void deleteUser(){
userMapper.deleteUser(6);
}
7.2 编写会话工具类
- 在静态代码块中创建会话工厂对象
- 编写静态方法得到会话对象,设置自动提交事务
- 编写静态方法得到会话对象,方法接收调用者传递的布尔类型参数,参数决定是否自动提交事务
- 编写静态方法接收会话对象,手动提交事务并且关闭会话
- 编写静态方法接收会话对象,回滚事务并且关闭会话
代码
package com.itheima.sh.utils;
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;
public class SqlSessionUtil {
private static SqlSessionFactory factory;
static {
//实例化工厂建造类
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//读取核心配置文件
try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
//创建工厂对象
factory = builder.build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 得到会话对象
*
* @return 会话对象 : 自动提交事务
*/
public static SqlSession getSession() {
return factory.openSession(true);
}
/**
* 得到会话对象
*
* @param isAutoCommit 是否自动提交事务
*/
public static SqlSession getSession(boolean isAutoCommit) {
return factory.openSession(isAutoCommit);
}
/*
* 提交事务并关闭session
* */
public static void commitAndClose(SqlSession session) {
if (session != null) {
session.commit();
session.close();
}
}
/*
* 回滚事务并关闭session
* */
public static void rollbackAndClose(SqlSession session) {
if (session != null) {
session.rollback();
session.close();
}
}
}
8、传入的参数
【1】parameterType
CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型。入参数据有以下几种类型:HashMap,基本数据类型(包装类),实体类(POJO);
在mybatis中入参的数据类型分为2种:
- 简单数据类型:int,long,String,Date;
- 复杂数据类型:POJO和Map;
说明:如果传递参数是数组或者集合,底层都会封装到Map集合中。
【示例】
public interface UserMapper {
//根据id查询
User queryById(Integer id);
//插入用户数据
int savetUser(User user);
}
【基本类型数据】
<select id="queryById" resultType="user" parameterType="int">
select * from user where id = #{id}
</select>
【pojo类型】
<insert id="savetUser" parameterType="User">
INSERT INTO user(...)
</insert>
说明: 对于parameterType属性可以不书写,那么MyBatis 就会通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。
【2】自增主键回填(了解)
【需求】
新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值。
【实现】
测试类代码:
@Test
public void saveUser() throws Exception {
User user = new User();
user.setUsername("蔡徐坤");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("上海");
userMapper.saveUser(user);
//查看新的数据的主键值
System.out.println(user.getId());//null
}
直接获取的结果是null。
有两种方式可以实现添加好数据直接将主键封装到实体类对象中,如下:
实现一:使用insert标签的子标签selectKey实现;(自学,了解,使用不多)
属性 | 说明 |
---|---|
keyColumn | 主键在表中对应的列名 |
keyProperty | 主键在实体类中对应的属性名 |
resultType | 主键的数据类型 |
order | BEFORE:会首先选择主键,设置 keyProperty 然后执行插入语句 AFTER: 在添加语句后执行查询主键的语句 |
【映射文件】
mysql中的函数:last_insert_id() 得到最后添加的主键
<!--
添加用户
因为增删改没有查询的结果集,所以不用配置resultType
有返回值,返回影响的行数
-->
<insert id="saveUser" parameterType="user">
insert into user values (null,#{username},#{birthday},#{sex},#{address})
<!--
keyColumn:主键在表中对应的列名
keyProperty:主键在实体类中对应的属性名
resultType:主键的数据类型
order:
BEFORE: 在添加语句前执行查询主键的语句
AFTER: 在添加语句后执行查询主键的语句
-->
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
</insert>
测试代码
通过getId()得到新增的主键值
/**
添加1个用户
在mybatis中增删改,默认是手动提交事务
1. 设置成自动提交 factory.openSession(true);
2. 自己手动提交 session.commit();
*/
@Test
public void saveUser() throws Exception {
User user = new User();
user.setUsername("蔡徐坤");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("上海");
userMapper.saveUser(user);
//查看新的数据的主键值
System.out.println(user.getId());//8
}
实现二:使用insert标签的属性 useGeneratedKeys,keyProperty,keyColumn实现;
属性 | 说明 |
---|---|
useGeneratedKeys | true 获取自动生成的主键,相当于select last_insert_id() |
keyColumn | 表中主键的列名 |
keyProperty | 实体类中主键的属性名 |
映射文件
<insert id="saveUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user values (null ,#{username},#{birthday},#{sex},#{address})
</insert>
- 说明:直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库,比如MySQL或SQL Server。
小结
- 添加用户使用哪个标签?
insert - 得到主键值有哪两种方式?
- 子标签:selectKey
- 在insert中添加属性
- 在mysql中得到主键的函数: last_insert_id()
【3】单个参数,多个参数(重点)
主要是针对简单类型的数据(int,string,long,Date)等数据进行入参处理。
单个参数
【接口传参】
方法示例,方法传入一个参数,如下
User queryById(Integer id);
【接收参数】
当接口方法传入一个参数时,mybatis不做特殊处理,只需要#{任意变量名}
都可接收;
1、通过#{参数名}接收
<!--根据id查询-->
<select id="queryById" resultType="User" parameterType="int">
select * from user where id = #{id}
</select>
2、通过#{任意变量名}接收 [不建议]
<!--根据id查询-->
<select id="queryById" resultType="User" parameterType="int">
select * from user where id = #{abc}
</select>
【结论】
当接口方法传入一个参数时,mybatis不做特殊处理,只需要#{任意变量名}都可接收;
【多个参数】
【接口传参】
//根据用户名和性别查询
User queryByUserNameAndSex(String userName, String sex);
【UserMapper.xml】
<select id="queryByUserNameAndSex" resultType="User">
select * from user where username=#{username} and sex=#{sex}
</select>
【测试类】
@Test
public void queryByUserNameAndSex() throws Exception {
User user = userMapper.queryByUserNameAndSex("孙悟空", "男");
System.out.println("user = " + user);
}
【结果】
上述报异常了,当传入多个参数时,mybatis底层进行了处理。我们需要按照如下几种方式解决异常:
【接收参数】
1、使用参数索引获取:arg0,arg1(了解)
<!--根据用户名和性别查询-->
<select id="queryByUserNameAndSex" resultType="User">
select * from user where username=#{arg0} and sex=#{arg1}
</select>
2、使用参数位置获取:param1,param2(了解)
<!--根据用户名和性别查询-->
<select id="queryByUserNameAndSex" resultType="User">
select * from user where username = #{param1} and sex = #{param2}
</select>
原理图例
3、使用命名参数获取,明确指定传入参数的名称:【掌握】
步骤一:在接口中传入参数时通过@Param指定参数名称
//根据用户名和性别查询
User queryByUserNameAndSex(@Param("username") String userName, @Param("sex") String sex);
步骤二:在接收参数时,通过指定的名称获取参数值;
<!--根据用户名和性别查询-->
<select id="queryByUserNameAndSex" resultType="User">
<!--#{username}这里的username是@Param("username"),也就是说@Param("标识符")标识符是什么,这里就写什么-->
select * from user where username=#{username} and sex=#{sex}
</select>
【小结】
多个参数时,接口中方法参数前加上 @Param("参数名") :
User queryByUserNameAndSex (@Param("username") String userName, @Param("sex") String sex);
xml文件中使用 #{参数名}获取
【4】pojo参数【重点】
使用pojo来传递参数
接口添加方法:
//新增
void saveUser(User user);
映射文件:
<!--新增-->
<insert id="saveUser">
insert into user values (null ,#{username},#{birthday},#{sex},#{address})
</insert>
说明: 接口方法传入pojo类型的数据时,mybatis底层直接使用pojo封装数据。 sql语句中 #{username}取值会到pojo中调用 getUsername(){ }
测试:
@Test
public void saveUser() throws Exception {
User user = new User();
user.setUsername("蔡徐坤");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("上海");
userMapper.saveUser(user);
}
【5】HashMap参数
当使用Map集合当做参数时,会将map的key作为参数名称来传递参数
案例:模拟用户登录,登录方法参数是Map集合,泛型都是String类型分别表示用户名和性别。
在UserMapper接口中添加以下方法:
/**
* 用户登陆,参数为map
* @return
*/
User login(Map<String,String> map);
UserMapper配置文件:
<!--
将map的key作为参数名称来传递参数
-->
<select id="login" resultType="User">
select * from user where username=#{username} and sex=#{sex}
</select>
测试用例:
@Test
public void login(){
Map<String, String> map = new HashMap<>();
map.put("username","孙悟空");
map.put("sex","男");
User user = userMapper.login(map);
System.out.println(user);
}
注意事项:
参数map中的key(键)是与SQL语句中 #{键} 的取值名称一致。
【6】入参小结:接口方法传入的参数
mybatis中能够接收2种类型的参数传入:
# 【1】基本类型数据:
单个参数: (Integer id);
mybatis底层不做处理,使用任意变量都可接收 #{abc} 建议直接写参数名
多个参数: (@Param("username")String username,@Param("sex")String sex);
# 【2】复杂数据类型:
1.pojo类型数据:mybatis底层直接使用pojo封装数据
sql取值: #{pojo的属性名}
JavaBean,Pojo 要根据规范书写,提供正常的getter,setter。
2.map集合数据:mybatis底层直接使用map封装参数
sql取值: #{map键名}
9、#{}和${}参数值获取的区分
我们之前一直在讲解接口中的方法传入的参数,参数值的获取指的是,statement获取接口方法中传入的参数。获取参数,有两种方式:#{}和${};
我们之前一直使用的是#{}的方式。
<select id="login" resultType="User">
select * from user where username=#{username} and sex=#{sex}
</select>
【1】#{}和${}取值
**#{}**
取值:
<select id="findUserById" resultType="User">
select *
from user
where id = #{abc};
</select>
mybatis底层处理:
==> Preparing: select * from user where id = ?;
因此,#{}取值:是以预编译的形式将参数设置到SQL语句中。PreparedStatement 防止SQL注入;
**${}**
取值:
【注意】${id} 获取id值时,必须使用命名参数取值:
如果不使用命名参数取值,即不在接口中加入@Param(“id”),就会报异常:
示例如下:
<select id="findUserById" resultType="User">
select *
from user
where id = ${id};
</select>
【接口方法】
//根据id查询
User queryById(@Param("id") Integer id);
【测试类】
@Test
public void queryById() throws Exception {
//获取mapper接口的动态代理实现
User user = userMapper.queryById(1);
System.out.println("user = " + user);
}
mybatis底层处理:
==> Preparing: select * from user where id = 1;
因此,${}取值:直接把获取到的参数值,拼接到sql语句中,会有安全问题;不能防止SQL注入;
【小结】
1、SQL语句中获取参数的方式:
#{xxx} sql:select * from user where id = ?
${xxx} sql:select * from user where id = 1
2、取值的相同点:
都能够获取接口方法传入的参数值
3、取值的不同点:
#{}取值:是以预编译的形式将参数设置到SQL语句中。PreparedStatement 防止SQL注入; --> PreparedStatement
${}取值:直接把获取到的参数值,拼接到sql语句中,会有安全问题;不能防止SQL注入; -->Statement
4、小结:
SQL传入参数的获取使用#{};
拼接参数使用${};
【2】${}取值的应用场景
在一些特殊的应用场景中,需要对SQL语句部分(不是参数)进行拼接,这个时候就必须使用${}来进行拼接,不能使用#{}.例如:
# 1、企业开发中随着数据量的增大,往往会将数据表按照年份进行分表,如:2017_user,2018_user....,对这些表进行查询就需要动态把年份传入进来,而年份是表名的一部分,并不是参数,JDBC无法对其预编译,所以只能使用${}进行拼接:
SELECT * FROM ${year}_user;
# 2、根据表名查询数据总记录数:
SELECT COUNT(*) FROM user
SELECT COUNT(*) FROM order
SELECT COUNT(*) FROM ${tableName}
简言之:如果需要设置到SQL中的不是查询的条件,只能使用${}拼接;
示例:
需求:查询user表中的总记录数。
【映射文件】:
<select id="selectCountByTableName" resultType="int">
select count(*) from ${tableName}
</select>
【接口】:
//需求:查询user表中的总记录数。
int selectCountByTableName(@Param("tableName") String table);
【测试类】
@Test
public void selectCountByTableName(){
int count = userMapper.selectCountByTableName("user");
System.out.println("count = " + count);
}
小结:
SQL中非参数部分的拼接使用${}
举例:
select * from user where username=#{username} and sex=#{sex} 这里 #{username} #{sex} 都属于参数部分,所以是#{}
select count(*) from ${tableName} 这里${tableName} 属于表名,不是参数部分,所以使用${}
【3】${}取值注意事项
1 【${}获取单个值】了解
${}
获取单个值时,最好是通过命名参数的形式获取。如果不指定参数的,也可以使用${value}
来获取传入的单个值;
传入参数:没有指定参数名称
id 没有使用 @Parma(“id”) 用美元符号方式获取不了。 ${id} 不行
但可以使用
获取参数:通过${value获取}
更好的做法给参数命名
User queryById(@Param("id") Integer id);
<select id="queryById" parameterType="int" resultType="User">
select *
from user
where id = ${id}
</select>
2 【${}获取配置文件中的值】
使用${}
获取数据还能从属性配置文件中获取数据。例如,${键名} 会得到应用的值
username=root
password=root
假如存在以下语句, ${username} 取到的值就是root了。
<!--根据用户名和性别查询-->
<select id="queryByUserNameAndSex" resultType="User">
SELECT * FROM user WHERE user_name = '${username}' AND sex = #{sex}
</select>
注意: ‘${username}’ 获取的是字符串,日期数据要加引号,如果是数值类型不用引号。
测试方法:
@Test
public void queryByUserNameAndPassword( ){
User user = userMapper.queryByUserNameAndSex("孙悟空", "男");
System.out.println("user = " + user);
}
执行测试:发现测试方法中传递的参数明明是孙悟空,却获取到了jdbc.properties资源文件中的root.
这种错误发生的根本原因是因为资源文件中的key和参数名称重名了,为了解决这个文件,我们只需要将资源文件中的key设置成唯一的不会被重复的key即可。
解决方案:在资源文件中的key中添加前缀
修改资源文件:
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/heima37
jdbc.username=root
jdbc.password=root
修改mybatis-config.xml:
<dataSource type="POOLED">
<!-- 配置连接信息 -->
<property name="driver" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
【小结】
# 1、#{}和${}取值的相同点:
都能够获取传入的命名参数
# 2、#{}和${}取值的不同点:
#{}: 以预编译的方式将参数设置到sql语句中 防止SQL注入 。可以自动加单引号
${}: 把参数直接拼接到sql语句中 不能够防止sql注入。不能自动加单引号。
# 3、具体应用:
#{}获取sql语句中的条件参数
${}应用于sql语句拼接和读取配置文件
# 4、 ${}注意事项
如果获取单个值,可以用${value}
${参数} 可能获取到properties文件中的值
扩展: mysql函数concat拼接使用
concat可以用来实现字符串的拼接。如下:
concat(string1,string2, ... ); -- 将括号中的数据拼接为一个字符串
案例:模糊查询地址中含有 ‘海’ 的用户
UserMapper接口
public interface UserMapper {
/*
需求:模糊查询地址中含有 海 的用户
*/
List<User> queryUsersByAddressLike(@Param("address")String address);
}
UserMapper.xml
<select id="queryUsersByAddressLike" resultType="User">
select * from user where address like concat('%',#{address},'%')
</select>
注意:... like '%#{address}%'
like条件不能使用#{},否则条件会变成 '%?%'
,必须使用${}
。
若使用${}
拼接sql语句: ... like '%${address}%'
条件等效于 ... like '%海%'
结果没问题但有sql注入问题 。因此建议使用concat函数结合#{}
使用。
测试类:
/*
需求:模糊查询地址中含有 海 的用户
List<User> queryUsersByAddressLike(@Param("address")String address);
*/
@Test
public void queryUsersByAddressLike() throws Exception {
List<User> list = mapper.queryUsersByAddressLike("海");
System.out.println("list = " + list);
}
10、结果映射
我们已经学习了入参和获取参数的方式,接下来学习最后的返回值。
在使用原生的JDBC操作时,对于查询结果集ResultSet,需要手动处理。mybatis框架提供了resultType和resultMap来对结果集进行封装。
注意:只要一个查询方法有返回值需要处理,那么 resultType和resultMap必须有一个
【1】resultType
该属性定义从sql语句中返回的期望类型其全类型名或别名。
注意如果返回的是集合,那应该设置为集合元素的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
1 返回值是简单类型
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
2 返回值为一个pojo(User)对象时
【定义resultType为User】
【使用User来接收返回值】
3 返回值为一个List<User>
时
当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型:
【映射文件】
【接口】
【测试类】
//1.使用接口对象调用接口中的方法
List<User> userList = mapper.findAllUsers();
//2.遍历集合
for (User user : userList) {
System.out.println(user);
}
4 返回值为map集合
【1】返回一条数据,封装到map中
此时列名会作为Map集合的key,结果作为Map集合的value保存。
案例:查询id是1的数据,将查询的结果封装到Map
接口方法:
//需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中
Map<String,Object> selectByIdReturnMap(Integer id);
SQL语句:
<select id="selectByIdReturnMap" resultType="map">
select * from user where id=#{id}
</select>
测试:
@Test
public void selectByIdReturnMap(){
Map<String, Object> map = userMapper.selectByIdReturnMap(1);
System.out.println("map = " + map);
}
结果:
map = {birthday=1980-10-24, address=花果山水帘洞, sex=男, id=1, username=孙悟空}
通过上述结果我们发现如果返回一条数据放到Map中,那么列名会作为Map集合的key,结果作为Map集合的value
【2】返回多条数据,封装到map中
案例:查询数据表所有的数据封装到Map<String,User>集合中
要求: Key值为一条记录的主键,Value值为pojo的对象.
如下所示:
接口方法:
接口方法上面通过注解 @MapKey指定key值封装的列数据
//需求:查询数据表所有的数据封装到Map<String,User>集合中
@MapKey("id")//把id字段当做Map的键使用
Map<String, User> selectReturnMap();
说明:需要在接口的方法上使用注解@MapKey指定数据表中哪一列作为Map集合的key,否则mybatis不知道具体哪个列作为Map集合的key.
SQL语句:
<select id="selectReturnMap" resultType="map">
select * from user
</select>
测试代码:
@Test
public void selectReturnMap(){
Map<String, User> map = userMapper.selectReturnMap();
System.out.println("map = " + map);
}
结果:
map = {1={birthday=1980-10-24, address=花果山水帘洞, sex=男, id=1, username=孙悟空}, 2={birthday=1992-11-12, address=白虎岭白骨洞, sex=女, id=2, username=白骨精}, 3={birthday=1983-05-20, address=福临山云栈洞, sex=男, id=3, username=猪八戒}, 4={birthday=1995-03-22, address=盤丝洞, sex=女, id=4, username=蜘蛛精}, 7={birthday=2020-06-05, address=上海, sex=男, id=7, username=蔡徐坤}, 8={birthday=2020-06-05, address=上海, sex=男, id=8, username=蔡徐坤}, 9={birthday=2020-06-05, address=上海, sex=男, id=9, username=蔡徐坤}, 10={birthday=2020-06-05, address=上海, sex=男, id=10, username=蔡徐坤}, 11={birthday=2020-06-06, address=上海, sex=男, id=11, username=蔡徐坤}}
小结
1.接口方法返回类型是简单类型(除了单列集合),那么在标签的resultType属性值中书写返回简单类型的类名或者别名
举例:
Integer show();====> resultType="int"
String show();====> resultType="string"
2.如果接口方法返回类型是简单类型的单列集合,那么在标签的resultType属性值中书写集合的泛型类型
举例:
List<User> show();====> resultType="User"
3.如果接口方法返回类型是复杂类型的pojo,那么在标签的resultType属性值中书写pojo类型
举例:
User show();====> resultType="User"
4.如果接口方法返回类型是复杂类型的Map,那么在标签的resultType属性值中书写map类型,但是分为返回的是单行数据还是多行数据:
如果是单行数据,不用做处理
如果是多行数据,在方法上使用注解@MapKey("数据表字段名")告知mybatis,哪个字段名的值作为map集合的key
举例: 如果是多行数据
@MapKey("id")
Map<Integer,Object> show();
【2】resultMap(掌握)
ResultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决两大问题:
- POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式,比如id和userId)
- 完成高级查询,比如说,一对一、一对多、多对多。(后面多表中会涉及到)
案例:假如查询数据的时候,查不到userName的信息怎么办?
分析原因:
JDBC和表字段两边名字不一致,造成mybatis无法填充对应的字段信息。比如数据库的字段名是user_name,而POJO中的属性名字是userName
解决方案1:在sql语句中使用别名
<select id="queryById" resultType="user" parameterType="int">
select *,name as userName from user where id = #{id}
</select>
解决方案2:参考驼峰匹配 —- mybatis-config.xml 的时候
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
注意:这种解决方案只能解决列名是下划线命名.
解决方案3:resultMap自定义映射
通过案例掌握resultMap的使用方式之一,手动配置实体类中属性和表中字段的映射关系
【案例】
使用resultMap完成结果集的对象封装(resultSet => JavaBean)
【实现步骤】
手动配置实体类属性和表字段映射关系的步骤如下:
1、 配置自定义结果集<resultMap>
2、 配置id映射
3、 配置其他普通属性的映射
步骤一:将驼峰匹配注释掉
一旦注释掉驼峰匹配,那么再通过queryUserById查询的结果中,用户名就无法封装了,结果为null,此时我们可以尝试使用ResultMap来解决这个问题。
步骤二:配置resultMap
resultMap标签的作用:自定义结果集,自行设置结果集的封装方式
属性 | 作用 |
---|---|
id | resultMap标签的唯一标识,不能重复,一般是用来被引用的 |
type | 结果集的封装类型 |
autoMapping | 操作单表时,不配置默认为true,如果pojo对象中的属性名称和表中字段名称相同,则自动映射。 |
在映射文件中自定义结果集类型:
<resultMap id="userResultMap" type="User" autoMapping="true">
<!--column 表示数据表列 property表示pojo的属性-->
<!--配置主键映射关系-->
<id column="id" property="id"></id>
<!--配置普通字段映射,用户名的映射关系-->
<result column="name" property="username"></result>
</resultMap>
步骤三:修改查询语句的statement
在查询语句的select标签中通过resultMap属性可以引用自定义结果集作为数据的封装方式。
<!--
resultMap属性:引用自定义结果集作为数据的封装方式.属性值是自定义resultMap标签的id属性值,这里表示通过id引入自定义的resultMap标签
-->
<select id="queryById" resultMap="userResultMap">
select * from user where id = #{id}
</select>
测试代码:
@Test
public void queryById() throws Exception {
//获取mapper接口的动态代理实现
User user = userMapper.queryById(1);
System.out.println("user = " + user);
}
注意:测试完记得将驼峰命名的配置重新开启,因为其他的测试方法还要用。
【小结】
resultMap可以用来手动配置属性和字段的映射关系:
属性:
1、id属性
定义唯一标识,用来被sql语句的声明引用的
2、type属性
配置结果集类型,将查询的数据往哪个类型中封装
3、autoMapping属性的值
为true时:在字段和属性名称相同时,会进行自动映射。如果不配置,则默认为true。
为false时:只针对resultMap中已经配置的字段作映射。
子标签:
1、id子标签
配置主键的映射关系
2、result子标签
配置其他普通属性和字段的映射关系