文章目录
一、框架
1、框架介绍
程序开发中的框架往往是对常见功能的封装,通常与具体业务无关,
也可以认为是软件的半成品。
程序框架理解为基础或者机械标准件(例如螺丝螺母标准的机械部件)。
一个框架是一组可复用的设计构件。
框架是一个半成品,软件是成品。我们在它的基础上开发出成品(软件)。
2、框架的作用
1.解决了技术通用的问题
在JavaEE体系中,有着各种各样的技术。不同的软件企业,根据自身的业务需求选择不同的技术,容易造成应用依赖技术,增加了项目开发实现的复杂性和技术风险性。而框架技术就可以解决上述问题。
2.提升了开发效率
企业项目中使用框架,只需要专注实现业务需求。使用框架的方便性,提升了开发效率。
3.提升了系统稳定性
一个成熟的框架,经过了在众多企业项目中的验证使用,稳定性有保障。
二、MyBatis框架
**官网及框架包下载:**http://www.mybatis.org/mybatis-3/
1、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 对象)`为数据库中的记录。
1)mybatis的优点
简单易学:mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个SQL映射文件即可。
使用灵活:Mybatis不会对应用程序或者数据库的现有设计强加任何影响。SQL语句写在XML里,便于统一管理和优化。
解除SQL与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易进行单元测试。SQL语句和代码的分离,提高了可维护性。
2)mybatis的不足
编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
Mybatis框架还是比较简陋(半自动化框架),功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
3)mybatis整体架构
4) mybatis的ORM方式
Object Relational Mapping 对象关系映射
Mybatis有两种映射方式:
1.通过XML映射;
2.通过注解;
2、mybatis框架开发
Mybatis存在2种开发方式
旧版本mybatis执行的方式(了解即可)
动态代理实现操纵数据库(掌握)
代码演示
0)Mybatis环境搭建
1.创建工程
2.导入依赖jar包;
3.创建接口和实体类;
4.配置mybatis全局配置文件和映射文件;
5.测试;
-- 数据准备
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;
1)mybatis入门(老式)开发【了解】
开发步骤:
第1步:创建项目工程
第2步:基于数据表编写实体类
第3步:编写mybatis配置文件 mybatis‐config.xml
第4步:编写映射文件 XxxMapper.xml
第5步:书写测试类
1、mybatis解决了三层(web,service,dao)中哪一层的问题?
dao
2、mybatis框架是对什么技术进行的封装?
JDBC
User类
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//getter and setter and toString
}
mybatis-config.xml
【在src下创建核心配置文件: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:///dbname"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--引入映射文件-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
UserMapper.xml
【在src下创建映射文件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.example.UserMapper">
<select id="selectUser" resultType="com.testmybatis.pojo.User">
select * from user where id = #{id}
</select>
</mapper>
MybatisTest测试类
public class TestAll {
@Test
public void test1() throws IOException {
//1.通过sql会话工厂构建类加载核心配置文件构建会话工厂
String resource="mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
//2.获取SqlSession会话对象 底层是对Connection封装
SqlSession sqlSession = sessionFactory.openSession();
//3.调用会话 api完成查询
User user = sqlSession.selectOne("org.example.UserMapper.selectUser", 1);
System.out.println(user);
//4.关闭资源
sqlSession.close();
}
}
log4j.properties
日志
### 设置Logger输出级别和输出目的地 ###
log4j.rootLogger=debug, stdout
### 把日志信息输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
Mybatis框架的核心类有哪些?
SqlSessionFactoryBuilder 会话工厂构建类 主要用来构建会话工厂的
SqlSessionFactory 会话工厂类 主要用来创建会话对象的;
SqlSession 会话对象 底层是对Connection连接对象的封装;
Mybatis老式的开发模式弊端
要使用指定的sql必须使用命名空间id进行字符串拼接,维护性比较差
并且sql用多少次,就拼接多少次,维护性比较差
2)mybatis的dao层(持久层)动态代理实现【重点】
Mybatis动态代理开发流程
1)导入依赖的jar (mybatis.jar 、jdbc驱动包 、log4j.jar)
2)定义pojo类型 (实体类)
3)定义操纵pojo类的接口
4)定义xml映射文件,然后与接口进行绑定
4.1 xml的命名空间与接口的全限定名称要一致
4.2 xml中的crud的标签id与接口中的方法名称要一致
5)配置核心配置文件 (1.配置数据源 2.加载xml映射文件)
6)通过SqlSessionFactoryBuilder类加载核心配置文件,构建会话工厂
7)通过会话工厂获取会话对象
8)通过会话对象调用**getMapper(接口名.class)**获取代理对象(正常使用接口完成数据库的操作)
9)释放资源,关闭连接session.close()
核心步骤
1)接口与xml映射文件绑定
接口名称与xml映射文件命名空间要一致
接口方法与xml中crud标签ID要一致
2)通过SqlSession获取代理对象
例如:sqlSession.getMapper(接口)
案例演示
用mybatis动态代理根据用户ID查询用户信息
UserMapper接口
第一步:新建接口
/**
* 根据用户id查询用户信息
* @param id
* 接口名称与xml映射文件命名空间要一致
* 接口方法与xml中crud标签ID要一致
*/
public interface UserMapper {
User findUserById(Integer id);
}
UserMapper.xml
第二步:将接口和映射文件绑定,在映射文件中书写SQL语句
接口与xml映射文件进行绑定**【注意】**
- 接口的全名称(带有包名)与xml的命名空间名称要一致
- 接口中的方法名称与xml的select标签的id要一致;
<?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.testmybatis.mapper.UserMapper">
<select id="findUserById" resultType="com.heima.pojo.User">
select * from user where id = #{id}
</select>
</mapper>
MybatisTest测试类
第三步:获取UserMapper的动态代理对象 完成查询
@Test
public void test2(){
// 1.加载核心配置文件,构建会话工厂
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
// 获取会话工厂构建类对象
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
// 2.获取会话对象
SqlSession sqlSession = sessionFactory.openSession();
// 3.获取接口代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findUserById(1);
System.out.println(user);
// 4.关闭资源
sqlSession.close();
}
3)mybatis操作事务
@Test
public void test05() throws IOException {
//基于mybatis核心配置文件,获取会话工厂对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//利用SqlSessionFactory,获取SqlSession
// 重载 SqlSession openSession(boolean var1);
//默认事务:手动提交 自动提交:设为true
SqlSession sqlSession = sqlSessionFactory.openSession(); // 使用默认事务:手动提交
//使用SqlSession执行sql
/*
<!-- 向数据表中插入数据 -->
<insert id="addUser">
insert into user(user_name,birthday,sex,address)
values (#{userName},#{birthday},#{sex},#{address})
</insert>
*/
// 获取接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User u = new User(0,"白龙马",
java.sql.Date.valueOf("1998-9-30"),"男","东海");
// 根据 id="" UserMapper调用接口中的方法 执行 sql 语句
int rowCount = userMapper.addUser(u); // 代理对象调用方法 被代理的方法进行增强
//处理结果
if(rowCount>0){
sqlSession.commit(); // 手动提交事务
System.out.println("插入成功!");
}
//关闭
sqlSession.close();
}
4)编写会话工具类
在静态代码块中创建会话工厂对象
编写静态方法得到会话对象
编写静态方法得到会话工厂对象
public class MybatisUtil {
//全局维护一个会话工厂
private static SqlSessionFactory sqlSessionFactory;
private static ThreadLocal<SqlSession> threadLocal=new ThreadLocal<>();
// 1、在静态代码块中加载mybatis核心配置文件,初始化sqlSessionFactory
static {
//抽取初始化工厂的方法
initFactory();
}
// 2、 封装初始化session工厂的方法
private static void initFactory() {
String resource="mybatis-config.xml";
InputStream in = null;
try {
in = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
}
/**
* 获取sqlSession对象
* @return
*/
// 3、静态方法得到会话对象
private static SqlSession getSqlSession(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession==null){
sqlSession=sqlSessionFactory.openSession();
//放回线程中 通过threadLocal为每一个线程都维护一个私有的会话对象,防止出现并发问题
threadLocal.set(sqlSession);
}
return sqlSession;
}
/**
* 封装获取动态代理对象的方法
* @param tClass
* @param <T>
* @return
*/
public static <T> T getMapper(Class<T> tClass){
SqlSession sqlSession = getSqlSession();
T mapper = sqlSession.getMapper(tClass);
return mapper;
}
/**
* 关闭资源
*/
public static void close(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession!=null){
sqlSession.close();
}
}
/**
* 事务提交
*/
public static void commit(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession!=null){
sqlSession.commit();
}
}
/**
* 事务回滚
*/
public static void rollback(){
SqlSession sqlSession = threadLocal.get();
if(sqlSession!=null){
sqlSession.rollback();
}
}
}
3、mybatis核心(标签)配置
mybatis全局配置介绍
mybatis-confifig.xml,是MyBatis的全局配置文件,包含全局配置信息,如数据库连接参数、插件等。整个框架中只需要一个即可。
说明:上述标签在实际使用过程中,要严格遵循使用顺序,否则报错
参考:https://mybatis.org/mybatis-3/zh/configuration.html
1)properties(引入外部文件)
**properties标签作用:**定义全局变量
properties定义全局变量2中方式
1 内部定义
<properties>
<property name="key" value="值"/>
</properties>
2 外部引入(推荐)
<properties resource="外部配置文件路径"/>
<properties>标签有两种使用方式
properties方式1
通过properties的子标签设置属性; 使用**${key}获取设置的属性值**;
<?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下的内部配置,配置全局参数 (了解即可)】
<properties>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="dburl" value="jdbc:mysql://127.0.0.1:3306/db1"/>
<property name="username" value="root"/>
<property name="passwd" value="1234"/>
</properties>
---------------------------------------------------------------
<environments default="dev">
<!--配置数据库环境-->
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
【使用${key}获取设置的属性值】
<property name="driver" value="${driverClass}"/>
<property name="url" value="${dburl}"/>
<property name="username" value="${username}"/>
<property name="password" value="${passwd}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--引入映射文件-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
properties方式2
通过properties标签 resource属性引入加载外部properties文件
首先创建jdbc.properties配置文件
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///day06_1
jdbc.userName=root
jdbc.password=1234
properties引入外部的配置文件, 加载value值
引入外部的配置文件
<properties resource="jdbc.properties"/>
加载value值
<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>
修改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>
---------------------------------------------------------------
<!-- 引入外部的properties文件 -->
<properties resource="jdbc.properties"/>
---------------------------------------------------------------
<environments default="dev">
<!--配置数据库环境-->
<environment id="dev">
<transactionManager type="JDBC"/>
<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>
</environment>
</environments>
<mappers>
<!--引入映射文件-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
2)settings(设置MyBatis 运行时行为)
settinngs是 MyBatis 中极为重要的调整设置,
它们会改变 MyBatis 的运行时行为。
说明:
settings参数有很多,我们先学习驼峰匹配mapUnderscoreToCamelCase
就是映射下划线到驼峰式命
名(自动把数据表中带有下划线的字段,转换为驼峰式命名)
修改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>
---------------------------------------------------------------
<!-- 引入外部的properties文件 -->
<properties resource="jdbc.properties"/>
<settings>
<!--开启驼峰映射:
table字段名称:user_name 自动变成:username或 userName
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
---------------------------------------------------------------
<environments default="dev">
<!--配置数据库环境-->
<environment id="dev">
<transactionManager type="JDBC"/>
<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>
</environment>
</environments>
<mappers>
<!--引入映射文件-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
开启驼峰自动映射的配置和作用
- 配置
- 作用
自动将表中字段比如:user_name 映射到pojo属性:username/userName
无需给sql中字段取别名
3)typeAliases(类型别名)
类型别名是给类的全限定名称(包名.类名))取一个短名称。
作用:减少类完全限定名的冗余。
通过设置一些短名来代替全限定名,有两种方式
方式1:使用typeAliases标签的子标签【 typeAlias手动设置类型别名】(逐一设置维护)
方式2:使用typeAliases标签的子标签 【package包扫描映射别名】(推荐)
代码演示:使用typeAliases标签的子标签 typeAlias简化UserMapper.xml中pojo类的全限定名称
首先 修改MyBatis-config.xml配置文件
<configuration>
<!-- 引入外部的properties文件 -->
<properties resource="jdbc.properties"/>
<settings>
<!--开启驼峰映射:
table字段名称:user_name 自动变成:username或 userName
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
---------------------------------------------------------------
<!--方式1:取别名,逐个取别名-->
<typeAliases>
<typeAlias type="com.itheima.pojo.User" alias="user"/> 逐个取别名
</typeAliases>
--------------------------------------------------------------
<!-- 方式2:通过扫包取指定包下的pojo类取别名 (推荐) -->
<typeAliases>
<!-- package name指定要取别名的包名 --> 对该包下的所有类取别名
<package name="com.itheima.pojo"/> 例如:自动设置别名为 User/user
</typeAliases>
---------------------------------------------------------------
<environments default="dev">
<!--配置数据库环境-->
<environment id="dev">
<transactionManager type="JDBC"/>
......................
......................
......................
</configuration>
其次 简化UserMapper.xml文件中pojo类的全限定名称
<?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.testmybatis.mapper.UserMapper">
---------------------------------------------
<select id="findUserById" resultType="user"> // resultType返回类型
对比
<select id="findUserById" resultType="com.heima.pojo.User"> // 包名+类名
---------------------------------------------
select * from user where id = #{id}
</select>
</mapper>
**代码演示:**完成根据id查询用户姓名的功能
1)定义接口方法:
/**
* 练习3:完成根据id查询用户姓名的功能;
* @param id
* @return
*/
String findNameByUserId(Integer id);
2)接口方法与xml映射文件sql进行绑定
<!--此时resultType中参数string使用的时String类的默认别名-->
<select id="findNameByUserId" resultType="string">
select user_name from user where id=#{id}
</select>
3)测试
public void test3(){
//1.加载核心配置文件,构建会话工厂
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sessionFactory = new
SqlSessionFactoryBuilder().build(in);
//2.获取会话对象
SqlSession sqlSession = sessionFactory.openSession();
//3.获取接口代理对象
UserMapper mapper =
sqlSession.getMapper(UserMapper.class);
String name = mapper.findNameByUserId(1);
System.out.println(name);
//4.关闭资源
sqlSession.close();
}
4)typeHandlers(类型处理器)
MyBatis 在设置预处理语句PreparedStatement)中的参数或从结果集中取出一个值时,
用类型处理器将获取到的值以合适的方式转换成 Java 类型。
Mybatis内置的一些类型处理器,企业开发中使用默认的类型处理器;
5) environments(环境配置)
MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置
尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一
虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离
父标签: environments(环境配置)
子标签:
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
案例:xml配置dev、test、online多环境数据源,并指定test为默认环境运行
6)mappers(映射器)
Mappers标签作用:提供了关联加载xml映射文件的配置功能
两种使用方式
1、加载XML映射文件,关联UserMapper.java接口
【方式1】<mapper resource="UserMapper.xml"/> // 从src下加载映射文件
【方式2】<mapper url=""/> // 从本地磁盘中加载映射文件,但是需要添加`file:///`协议
/** 说明:如果项目采用基于xml的开发模式,建议使用方式1开发 */
2、加载接口,关联映射文件
条件:1、接口名和映射文件名保持一致; 2、路径保持一致;
【方式3】<mapper class=“com.test.mybatis.dao.UserMapper”/> //加载接口
【方式4】<package name="com.test.mybatis.dao"/> //批量加载class
/** 说明:如果基于注解开发的开发的话,推荐使用方式4开发 */
代码演示
1) 使用mapper下resource属性加载xml映射文件关联接口(掌握)
<mappers>
<!--通过resource属性加载工程下相对路径下的映射文件-->
<mapper resource="mapper/UserMapper.xml"/> // 从src下加载映射文件
</mappers>
2) 使用完全限定资源符加载映射文件(了解即可) <mapper url="file:///E:\day06\day06_1\src\mapper\UserMapper.xml"/>
3) 使用映射器接口实现类的完全限定类名 (了解即可)
前提条件:
1、接口名和映射文件名保持一致;
2、路径保持一致;
3、映射文件中的namespace必须与接口的全限定名称一致;
<mapper class="com.itcast.mapper.UserMapper"/>
4) 加载接口,关联映射文件方式package(掌握)
前提条件:
1、接口名和映射文件名保持一致;
2、路径保持一致;
3、映射文件中的namespace必须与接口的全限定名称一致;
<package name="com.itcast.mapper"/> // 接口的包路径 批量加载class
1、开发时mybatis-config.xml中常用的2种加载映射文件XxxMapper.xml的方式
①通过resource属性,相对路径加载(基于xml开发推荐的)
②通过package扫包方式(基于注解开发推荐使用)
约束条件:
1 接口名称与xml映射文件名称要一致;
2 接口路径与xml映射文件路径也要一致;
===================================================
2、基于package扫描的原理?
前提:
1.接口名称与xml映射文件名称要一致;
2.接口路径与xml映射文件路径也要一致;
通过package指定接口的路径之后,mybatis就会加载这个包下的接口,获取
接口的名称,因为接口名称和路径与xml一致,所以也就获取xml映射文件;
===================================================
3、思考:基于package扫描存在的缺点?
约束条件太多了;
java文件与xml文件存放在同一个路径下,可维护性差;
4、Mybatis映射文件配置
Mapper映射文件中定义了操作数据库的sql,每一个sql都被包含在一个statement中
映射文件是mybatis操作数据库的核心
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
说明:
映射文件中需要直接书写SQL语句对数据库进行操作,对数据库操作SQL语句主要有CRUD四类。
这四类对应到映射文件中的配置为四类标签:
select
,insert
,update
,delete
1)slelect标签
根据id查询用户生日
1) 定义接口方法
/**
* 根据id查询用户生日
* @param uid
* @return
*/
public abstract Date findBirthdayById(int uid);
2) xml文件sql绑定接口方法
<!-- 根据用户id查询用户生日 -->
<select id="findBirthdayById" resultType="java.sql.Date">
select birthday from user where id = #{id}
</select>
3) 测试
@Test
public void test01() throws IOException {
//1.通过sql会话工厂构建类加载核心配置文件构建会话工厂
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
//2.获取SqlSession会话对象 底层是对Connection封装
SqlSession sqlSession = sessionFactory.openSession();
//3. 获取接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Date birthday = userMapper.findBirthdayById(1);
System.out.println(birthday);
//4. 关闭资源
sqlSession.close();
}
2)insert标签
向数据库添加用户
1) 定义接口方法:
/**
* 添加用户
* 返回值时影响的行数
* @param user
* @return
*/
Integer addUser(User user);
2) 绑定映射文件
<!--比如:拿到username 进行方法组装getUsername-->
<!--结论:如果传入的时pojo对象,那么参数名称与pojo的属性名称要一致-->
<insert id="addUser">
insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert
3) 测试
@Test
public void test02() throws IOException {
//1.通过sql会话工厂构建类加载核心配置文件构建会话工厂
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new
SqlSessionFactoryBuilder().build(in);
//2.获取SqlSession会话对象 底层是对Connection封装
SqlSession sqlSession = sessionFactory.openSession();
//3. 获取接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用方法,添加用户
int rowCount = userMapper.addUser(new User(null, "唐憎",
Date.valueOf("2020-12-15"), "男", "长安"));
System.out.println(rowCount);
//手动提交事务(MyBatis默认事务为手动提交)
sqlSession.commit();
//4. 关闭资源
sqlSession.close();
}
3)update标签
1) 定义接口方法
/**
* 更新用户
* @param user
* @return
*/
void updateUser(User user);
2) 绑定映射文件
<update id="updateUser">
update user set user_name=#{username},birthday=#{birthday},sex=#{sex},address=
#{address} where id=#{id}
</update>
4)delecte
1) 定义接口方法
/**
* 根据id删除用户
* @param id
*/
void deleteById(Integer id);
2) 映射文件绑定接口方法
<delete id="deleteById">
delete from user where id=#{id}
</delete>
小结
MyBatis框架:
- 框架一个半成品
- 程序是在一个半成品的项目上开发程序
- 学习半成品项目怎么使用(学习怎么使用MyBatis)
MyBatis的使用流程:
- 1、创建项目
- 2、在项目中导入mybatis的jar文件(mybatis是dao层框架,需和数据库交互,需要数据库的jar文件)
- 3、编写核心配置文件: mybatis-config.xml (mybatis加载时要使用)
- 连接数据库要使用的参数:驱动、url、登录名、密码
- mybatis框架使用的全局配置:properties、settings、typeAliases
- 4、编写实体类
- 5、编写映射文件:XxxxMapper.xml (用来书写sql语句)
- 6、创建一个和映射文件关联的接口
- 映射文件中的 名称空间的值 = 接口的全名称
- 映射文件中的 CRUD标签的id值 = 接口中的方法名
- 6、获取MyBabtis中的SqlSession对象(Connection、CRUD操作)
- 使用SqlSessionFactory对象,获取SqlSession对象
- 7、利用SqlSession对象,执行CRUD的sql语句
- 8、处理sql执行结果
- 9、关闭SqlSession
相对路径问题
mybatis-config.xml文件
<!-- 1、使用Properties标符,引入外部的资源文件 -->
<properties resource="config/jdbc.properties"/>
<package name="com.itheima.mb.mapper"/>
<mapper resource="config/UserMapper.xml"/>
------------------------------------------------------
UserMapper.xml文件
<!--// 命名空间为 映射文件的相对src目录的路径 【加载映射接口,使用其中抽象方法】 -->
<mapper namespace="com.itheima.mb.mapper.UserMapper">
------------------------------------------------------
测试方法中:
String resource = "config/mybatis-config.xml"; // 相对于 src 下的相对路径
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
一般mybatis-config.xml配置文件
1)核心配置文件配置标签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>
<!-- 1、使用Properties标符,引入外部的资源文件 -->
<properties resource="jdbc.properties"/>
<!--2、mapUnderscoreToCamelCase自动把带有下划线的字段名,转换为驼峰命名 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 3、这里使用package标签 : 指定包下的所有类型,都具有别名-->
<!--<typeAlias type="com.itheima.mb.pojo.User" alias="User"/>-->
<!-- 需要给指定的类型,取别名。 如果有多个指定类型时,需要书写多个typeAlia -->
<!-- 使用package,指定包名。 指定包下的所有类型,都具有别名
eg: com.pojo.User com.pojo.Student
指定包名:com.pojo 那么【 com.pojo.User 】的别名为: user 或 User
-->
<typeAliases>
<package name="com.itheima.mb.pojo"/>
</typeAliases>
<!-- 4、environments指定开发环境dev / test-->
<environments default="dev">
<!-- 1. 开发数据库环境-->
<environment id="dev">
<!-- 1.1 事务管理器: 数据库中的事务由JDBC完成 -->
<transactionManager type="JDBC"/>
<!-- 1.2 type="POOLED" 表示: 使用mybaits默认的连接池 -->
<dataSource type="POOLED">
<!-- 1.3 给(属性)参数赋值 :${key} 获取到jdbc.properties属性集配置文件参数Value值 -->
<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>
</environment>
<!--2. 测试数据库环境-->
<environment id="test">
<transactionManager type="JDBC"/>
<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>
</environment>
</environments>
<!-- 5、这里使用package标签:把指定接口下所有的接口引入 适用于:注解方式 -->
<!-- 把指定接口下所有的接口引入
加载接口,关联映射文件方式package(掌握)
<package name="com.itheima.mb.mapper"/> 接口的包路径
前提条件:
1、接口名和映射文件名保持一致;
2、路径保持一致;
3、映射文件中的namespace必须与接口的全限定名称一致;
适用于:注解方式 -->
<!-- 引入映射文件 适用于: xml方式
<mapper resource="com/itheima/mb/mapper/UserMapper.xml"/> 文件的相关路径
<mapper class="com.itheima.mb.mapper.UserMapper" /> 接口实现类的完全限定类名
-->
<mappers>
<package name="com.itheima.mb.mapper"/>
</mappers>
</configuration>
一般XxxMapper.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">
<!--// 命名空间为 映射文件的相对src目录的路径 【加载映射接口,使用其中抽象方法】 -->
<mapper namespace="cn.mybatis.demo.mapper.UserMapper">
<!-- 1、查询的语句 : 根据用户id查询用户信息【因为引入了package标签】
加typeAliases(类型别名) 不用写 包名+类名,直接写resultType="user" / "User" -->
<select id="findUserById" resultType="user">
select id,user_name ,birthday,sex,address from user where id= #{id}
</select>
<!--2、根据用户id查询用户姓名 -->
<!-- 如果不写 resultType="string" 报错如下
Cause: org.apache.ibatis.executor.ExecutorException:
A query was run and no Result Maps were found for the Mapped Statement
'cn.mybatis.demo.mapper.UserMapper.findNameByUserId'. -->
<select id="findNameByUserId" resultType="string">
select user_name from user where id=#{id}
</select>
<!--3、根据用户id查询用户生日-->
<select id="findBirthdayById" resultType="Date">
select birthday from user where id = #{id}
</select>
<!-- 4、向数据表中插入数据 -->
<insert id="addUser">
insert into user(id,user_name,birthday,sex,address)
values
(#{id},#{userName},#{birthday},#{sex},#{address})
</insert>
<!-- 5、根据用户id删除数据 -->
<delete id="deleteUserById">
delete from user where id = #{id}
</delete>
</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.example.UserMapper">
<select id="方法名" resultType="pojo名称默认首字符小写/_基本类型/jdk对象类型小写">
select * from user where id = #{id}
</select>
</mapper>
==========================================================================
<select id="findBirthdayById" resultType="java.sql.Date">
select birthday from user where id = #{id}
</select>
<insert id="insertAuthor">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor">
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
</update>
<delete id="deleteAuthor">
delete from Author where id = #{id}
</delete>
1.select
格式:
<select id="对应接口中的方法名称" resultType="pojo名称默认首字符小写/_基本类型/jdk对象类型小写">
select * from t where id=#{任意变量}
</select>
2.insert
格式:
<insert id="xx">
insert into t values(null,#{对应pojo中属性名称},....)
</insert>
3.update
格式:
<update id="xxx">
update t set 字段1=#{对应pojo中属性名称},.... where id=#{id}
</update>
4.delete
格式:
<delete id="xxx">
delete from t where id=#{id}
</delete>
总结案例:根据给定的表结构,完成sql操作功能
表:Student
字段名 | 数据类型 | 说明 |
---|---|---|
stu_id | int | 学生id 主键,自动增长 |
stu_name | varchar(30) | 学生姓名 |
stu_age | int | 学生年龄 |
stu_gender | char(2) | 学生性别 默认值:男 |
stu_phone | varchar(20) | 手机号 |
stu_address | varchar(50) | 地址 |
使用MyBatis技术,实现以下需求
需求1:向Student表中插入学生信息
需求2:获取学号为2的学生信息
需求3:修改’小明’的地址为"超级无敌大上海之XXX程序员"
需求4:如手机号’13800138000’存在,则删除该手机号学生信息
需求5:获取所有学生信息
测试类
package homework.main;
import com.itheima.mb.pojo.User;
import homework.mapper.StudentMapper;
import homework.pojo.Student;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import util.MybatisUtil;
import java.util.List;
public class StudentTest {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class);
// 1、向Student表中插入学生信息 [insert]
@Test
public void insertTest() {
System.out.println("--------------1、insertStudent----------------");
Student student = new Student(null, "景甜", 23,
"女", "15211314520", "西安");
int rowInsert = stuMapper.insertStudent(student);
if (rowInsert > 0) {
MybatisUtil.commit(); // 手动提交事务
System.out.println("Insert Successful!");
}
MybatisUtil.close();
}
// 2、获取学号为2的学生信息 [select]
// 5、 获取所有学生信息 [select]
@Test
public void selectTest() {
System.out.println("--------------2、selectUserById----------------");
Student student = stuMapper.selectStudentById(1);
System.out.println(student);
System.out.println("--------------5、selectUserById----------------");
List<Student> list = stuMapper.selectStudentAll();
// System.out.println(list);
for (Student stu : list) {
System.out.println(stu);
}
MybatisUtil.close();
}
// 3. 修改'小明'的地址为"上海市航头镇18号黑马程序员" [update]
@Test
public void updateTest() {
System.out.println("--------------3、updateAddressByName----------------");
int rowUpdate = stuMapper.updateAddressByName("上海市航头镇18号黑马程序", "小明");
if (rowUpdate > 0) {
MybatisUtil.commit();
System.out.println("Update Successful!");
}
MybatisUtil.close();
}
// 4. 如手机号'13800138000'存在,则删除该手机号学生信息 [delete]
@Test
public void deleteTest() {
System.out.println("--------------4、deleteUSerByPhone----------------");
int rowDelete = stuMapper.deleteStudentByPhone("13800138000");
if (rowDelete > 0) {
MybatisUtil.commit();
System.out.println("Delete Successful!");
}
MybatisUtil.close();
}
}
StudentMapper映射接口
package homework.mapper;
import homework.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface StudentMapper {
// 1. 向Student表中插入学生信息 [insert]
abstract int insertStudent(Student student);
// 2. 获取学号为2的学生信息 [select]
abstract Student selectStudentById(int stuId);
// 3. 修改'小明'的地址为"上海市航头镇18号黑马程序员" [update]
abstract int updateAddressByName(@Param("stuAddress") String stuAddress, @Param("stuName") String stuName);
// 4. 如手机号'13800138000'存在,则删除该手机号学生信息 [delete]
abstract int deleteStudentByPhone(String stuPhone);
// 5. 获取所有学生信息 [select]
public abstract List<Student> selectStudentAll();
}
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>
<!-- 1、使用Properties标符,引入外部的资源文件 -->
<properties resource="config/jdbc.properties"/>
<!--2、mapUnderscoreToCamelCase 自动把带有下划线的数据库字段名转换为驼峰命名,
赋值给相应的PoJo类属性且不区分大小写
eg: user_name ==> userName/username 不写settings标签 属性得不到 数据库字段的值
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 3、这里使用package标签 : 指定包下的所有类型,都具有别名-->
<!--<typeAlias type="com.itheima.mb.pojo.User" alias="User"/>-->
<!-- 需要给指定的类型,取别名。 如果有多个指定类型时,需要书写多个typeAlia -->
<!-- 使用package,指定包名。 指定包下的所有类型,都具有别名
eg: com.pojo.User com.pojo.Student
指定包名:com.pojo 那么【 com.pojo.User 】的别名为: user 或 User
-->
<typeAliases>
<package name="homework.pojo"/>
<!-- <package name="com.itheima.mb.pojo"/>-->
<!-- <package name="cn.mybatis.demo.pojo"/>-->
</typeAliases>
<!-- 4、environments指定开发环境dev / test-->
<environments default="dev">
<!-- 1. 开发数据库环境-->
<environment id="dev">
<!-- 1.2 事务管理器: 数据库中的事务由JDBC完成 -->
<transactionManager type="JDBC"/>
<!-- 1.3 type="POOLED" 表示: 使用mybaits默认的连接池 -->
<dataSource type="POOLED">
<!-- 1.4 给(属性)参数赋值 :${key} 获取到jdbc.properties属性集配置文件参数Value值 -->
<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>
</environment>
<!--2. 测试数据库环境-->
<environment id="test">
<transactionManager type="JDBC"/>
<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>
</environment>
</environments>
<!-- 5、这里使用package标签:把指定接口下所有的接口引入 适用于:注解方式 -->
<!-- 把指定接口下所有的接口引入
加载接口,关联映射文件方式package(掌握)
<package name="com.itheima.mb.mapper"/> 接口的包路径
前提条件:
1、接口名和映射文件名保持一致;
2、路径保持一致;
3、映射文件中的namespace必须与接口的全限定名称一致;
适用于:注解方式 -->
<!-- 引入映射文件 适用于: xml方式
<mapper resource="com/itheima/mb/mapper/UserMapper.xml"/> 文件的相关路径
<mapper class="com.itheima.mb.mapper.UserMapper" /> 接口实现类的完全限定类名
-->
<mappers>
<!-- <package name="com.itheima.mb.mapper"/>-->
<!-- 1、ithima UserMapper映射文件的相关路径-->
<!-- <mapper resource="com/itheima/mb/mapper/UserMapper.xml"/> -->
<!-- <package name="com.itheima.mb.mapper"/>-->
<!-- 2、mybatis UserMapper映射文件的相关路径-->
<!-- <package name="cn.mybatis.demo.mapper"/>-->
<!-- 3、homework StudentMapper映射文件的相关路径-->
<package name="homework.mapper"/>
</mappers>
</configuration>
StudentMapper.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">
<!-- 命名空间为 映射文件的相对src目录的路径 【加载映射接口,使用其中抽象方法】 -->
<mapper namespace="homework.mapper.StudentMapper">
<!-- 1、向Student表中插入学生信息 加了typeAliases(类型别名) [insert]
【引入了package标签】所以不用写 包名+类名,直接写resultType="user" / "User" -->
<insert id="insertStudent">
insert into student(stu_id,stu_name,stu_age,stu_gender,stu_phone,stu_address)
values(#{stuId},#{stuName},#{stuAge},#{stuGender},#{stuPhone},#{stuAddress})
</insert>
<!--2、获取学号为2的学生信息 [select]-->
<select id="selectStudentById" resultType="Student">
select * from student where stu_id = #{stuId}
</select>
<!--3、修改'小明'的地址为"上海市航头镇18号黑马程序员"[update]-->
<update id="updateAddressByName" >
update student set stu_address = #{stuAddress} where stu_name = #{stuName}
</update>
<!-- 4、如手机号'13800138000'存在,则删除该手机号学生信息 [delete] -->
<delete id="deleteStudentByPhone">
delete from student where stu_phone = #{stuPhone}
</delete>
<!-- 5、获取所有学生信息 [select] -->
<select id="selectStudentAll" resultType="Student">
select * from student
</select>
</mapper>
三、Mybatis映射文件配置
1、参数
1)参数值的传入
01)parameterType(了解)
CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型。
入参数据有以下几种类型:
HashMap,基本数据类型(包装类),实体类;
设置传入这条语句的`参数类的完全限定名或别名。
这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数类型。
`在mybatis中入参的数据类型分为2大类:
1. 基本数据类型:int,string,long,Date等;
2. 复杂数据类型:类(pojo)和Map;
说明:如果传递参数是数组或者集合,底层都会封装到Map集合中。
【基本类型数据】
<!--根据id查询-->
<!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名.MyBatis 可以 通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推 断出具体传入语句的参数类型。-->
<select id="findById" resultType="user" parameterType="int"> select * from user where id = #{id}
</select>
【pojo类型】
底层原理: User类:user_name属性,自动生成setter或者getter方法时,getUserName 当我们向xml传入#{user_Name}---->getUser_Name--->利用反射根据方法名称获取方法对象--- >报反射异常 综上: 1)传入的时pojo的话,传入xml的变量名称与pojo类下属性名称要一致; 2)pojo中属性名称定义要尽量遵循驼峰命名,或者在自动生成getter或者setter方法时,自己手动纠 正;
<insert id="savetUser" parameterType="User">
INSERT INTO user(...) values(#{userName},...);
</insert>
说明:
对于parameterType属性可以不书写,
那么MyBatis 就会通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。
02)自增主键回填(了解)
新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值
直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库。
如:MySQL、SQL Server
`使用insert标签的子标签selectKey+last_insert_id()函数实现实现自增主键回填
keyColumn 主键在表中对应的列名
keyProperty 主键在实体类中对应的属性名
resultType 主键的数据类型
order BEFORE 会首先选择主键,设置 keyProperty 然后执行插入语句 AFTER: 在添加语句后执 行查询主键的语句
案例演示:
接口:
/**
* 添加用户
* 返回值是影响的行数
* @param user
* @return
*/
Integer addUserAndGetFkId(User user);
映射文件:
<!--
selectKey:表示查询主键字段的标签 keyColumn:表示表中字段名称,一般指主键
名称
keyProperty="id":表示pojo类中对应主键的属性名称
order="AFTER":表示在操作之前或者之后获取主键值
-->
----------------------------------------------------------------------
1、自增主键回填: selectKey标签 + last_insert_id()函数
<insert id="addUserAndGetFkId">
insert into user values(null,#{username},#{birthday},#{sex},#{address})
<selectKey keyColumn="id" keyProperty="id" resultType="int"
order="AFTER">
select last_insert_id()
</selectKey>
</insert>
----------------------------------------------------------------------
2、自增主键回填: insert标签的属性useGeneratedKeys,keyProperty,keyColumn
<insert id="addUserAndGetFkId2" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert>
----------------------------------------------------------------------
测试:
@Test
public void work01() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println("----------添加姓名为张三的用户----------------");
User user = new User();
user.setuName("王五");
Integer row1 = mapper.addUserAndGetFkId(user);
System.out.println("影响行:"+row1); // 1
// 插入完成之后,查询的主键值,会封装到User对象中的id属性中
Integer uId = user.getuId();
System.out.println("主键值" + uId);
}
03)Mybatis入参个数是单参和多参(掌握)
单个参数
【映射接口传参】
User queryById(Integer id); //接口方法传入一个参数
【映射文件接收参数】
<!--根据id查询-->
<select id="queryById" resultType="User" parameterType="int">
select * from user where id = #{id}
</select>
// 在xml中通过#{任意变量名}可以接收到参数
接口传入时是单个参数,可在xml中使用任意变量取接收但是不建议乱写,最好见名知意。
参类型是Pojo和Map的多参
三种方案解决入参是多个参数的问题
方式1:使用参数索引获取:arg0,arg1 (了解即可)
<select id="findUsersByUserNameAndSex" resultType="user">
select * from user where user_name=#{arg0} and sex=#{arg1}
</select>
方式2:使用参数位置获取:param1,param2 (了解即可)
<select id="findUsersByUserNameAndSex" resultType="user">
select * from user where user_name=#{param1} and sex=#{param2}
</select>
方式3:使用命名参数获取,在接口处明确指定传入参数的名称 (掌握)
/**
* 需求:根据用户名和性别查询用户
* @param name
* @param sex
* @return
*/
第一步:接口处明确指定传入参数的名称
List<User> findUsersByUserNameAndSex(@Param("name") String name,@Param("sex") String sex);
第二步:在接收参数时,通过指定的名称获取参数值
<select id="findUsersByUserNameAndSex" resultType="user">
select * from user where user_name=#{name} and sex=#{sex}
</select>
2)参数值的获取
获取参数的方式
在mybatis中获取传入参数值的方式有两种:#{} 和 ${}
1、#{}取值
使用#{}的sql是进行预编译的,可以防止sql注入
2、${}取值
• ${id} 获取id值时,必须使用命名参数取值@param
• 如果是取单个值,也可使用${value}获取
• 参数值直接拼接到sql中,会有sql注入的风险
#{}:sql进行预编译处理,防止sql注入
${}:参数与sql直接拼接,有sql注入的风险
01)${}取值的应用场景
获取的是pojo类型参数
接口方法传入pojo类型的数据时,
xml中使用#{pojo属性名称}可直接获取传入对象对应的属性值
1、【接口】
/**
* 插入功能
* @param user
*/
void saveUser(User user);
2、【映射文件】
<insert id="saveUser">
insert into user values(null,#{username},#{age},#{birthday},#{sex},#{address})
</insert>
入参是Map类型
接口方法传入Map类型的数据时,
xml中使用#{map中key}可直接获取map中的value值
1、【接口:】
User queryByNameAndSex(Map map);
2、【映射文件:】
<select id=“queryByNameAndSex" resultType="user" parameterType="map"> select * from user where user_name=#{name} and sex=#{sex}
</select>
02)${}取值的应用场景
在一些特殊的应用场景中,需要对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中的不是查询的条件,只能使用${}拼接
**案例演示:**根据输入的表名统计指定表下的总记录数
2、结果映射
1)resultType 结果映射
简单的结果映射
在使用原生的JDBC操作时,对于结果集ResultSet,需要手动处理;
mybatis框架提供了resultType和resultMap来对结果集进行封装;
只要一个方法有返回值需要处理,那么 resultType和resultMap必须有一个;
从sql语句中返回的期望类型的类的完全限定名或别名。
注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。
可以使用 resultType 或 resultMap,但不能同时使用。
`① 返回值是基本类型
例如:
int ,string ===>resultType="写入对应的基本类型别名或者全限定名即可"
1)基本类型 int short double ... 别名: _基本类型名称
2)包装类 类 String ArrayList .... 别名: 类名首字母小写
3)自定义类 扫包取别名 类首字母小写(大写也可以)
`② 返回值为一个pojo(User)对象时
1)表中列明要与pojo中属性名称一致
2)若表中列名与pojo中属性名称不一致,可使用as取别名使其一致即可
3)如果满足驼峰映射,也可以开启驼峰映射设置
`③返回值为一个List<User>时
当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型
Map的结果形式
- map接收单条记录
map中的
key就是查询的表的字段名称,
如果使用as区别名,那么可以就是对应的别名的名称;
map接收多条记录
需要指定作为key的字段,一般是主键字段 @MapKey(“指定字段名称”)
指定每一条记录封装的对象类型;
代码演示1 ,返回一条数据
查询id是1的数据,
将查询的结果封装到Map<String,Object>中返回一条数据,封装到map中
1) 定义接口
/**
* 需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中
* @param id
* @return
*/
Map<String,Object> findMapById(@Param("id") Integer id);
2) 定义映射文件配置
<select id="findMapById" resultType="map">
select id,user_name as userName,address from user where id=#{id}
</select>
3) 结果
{address="xxxx",id="xxx",userName="xxx"}
代码演示2 ,返回多条数据
查询数据表所有的数据封装到Map<Integer,User>集合中,
Key值为一条记录的主键,Value值为pojo的对象
2)resultMap结果映射
1、正常开发中,数据库字段名称与Pojo类属性名称不一致时,一般通过驼峰映射或者AS关键字取别名可以搞定,但是很多场景下,对于复杂的orm映射,上述的2种方式就不能适用了;
2、ResultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决复杂映射问题:
- POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式,比如id和userId)
- 完成高级查询,比如说,一对一、一对多、多对多。
resultMap完成结果集的封装步骤(resultSet -->JavaBean)
自定义配置实体类属性和表字段映射关系的步骤如下:
1、 配置自定义结果集
2、 配置id映射
3、 配置其他普通属性的映射
**步骤一:**将驼峰匹配注释掉
一旦注释掉驼峰匹配,那么再通过findById查询的结果中,用户名就无法封装了,此时我们可以尝试使用ResultMap来解决这个问题
<settings>
<!--作用:表:user_name 类:userName/username 自动映射-->
<setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
**步骤二:**配置resultMap
resultMap标签的作用:自定义结果集,自行设置结果集的封装方式
id属性:resultMap标签的唯一标识,不能重复,一般是用来被引用的
type属性:结果集的封装类型
autoMapping属性:操作单表时,不配置默认为true,如果pojo对象中的属性名称和表中字段名称相同,则自动映射
<!--type="user" 表示结果集的封装类型是user-->
<resultMap id="userResultMap" type="user" autoMapping="true">
<!--配置主键映射关系-->
<id column="id" property="id"></id>
<!--配置用户名的映射关系 column 表示数据表列 property表示pojo的属性--> <result column="user_name" property="name"></result>
</resultMap>
代码演示
使用resultMap完成根据用户id查询用户信息的结果集的封装(resultSet JavaBean)
小结
1、resultMap标签映射流程解析:
2、resultMap标签的作用?
1)提高了代码的复用性;
2)结果集映射的;(将查询的结果映射到pojo类下)
3、resultMap有哪些属性和子标签?
<resultMap id="唯一标识" type="映射的pojo类" autoMapping="true">
<!--主键字段映射-->
<id column="表中主键字段" property="pojo类中主键对应的属性名称"/>
<!--普通字段映射-->
<result column="表中非主键字段" property="pojo类中非主键对应的属性名称"/>
</resultMap>
3、独立文件中的SQL片段
sql标签可以定义一个sql片段,在需要使用该sql片段的地方
通过<include refid="sql片段id"/>标签来使用
【1】定义SQL片段
<sql id="userCommonSql">
id,user_name,birthday,sex,address
</sql>
注意:SQL片段必须设置id属性;
【2】使用SQL片段
在SQL语句中通过`<include>`标签引入SQL片段
<select id="findById" resultMap="userMap">
select <include refid="userCommonSql"/> from user where id=#{id}
</select
sql标签可以定义一个sql片段,在需要使用该sql片段的地方,
通过<includerefid=“sql片段id”/>标签来使用。很多时候同一个sql片段,可能在多个映射文件中都有使用,如果每一个映射文件都编写一个相同的sql就比较麻烦,
因此可以将通用的sql片段都定义在一个专门存放sql片段的映射文件中,然后由其他映射文件引用它即可。
如下,在src目录下新增CommonSQL.xml文件
【1】 新建SQL片段文件【CommonSQL.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="CommonSql">
<sql id="userSql">
id,user_name,birthday,sex,address
</sql>
<sql id="empSql">
eid,ename,age
</sql>
</mapper>
【2】在mybatis核心配置文件mybatis‐config.xml引入SQL片段文件。
定义好sql片段的映射文件之后,接下来就该使用它了,首先应该把该映射文件引入到mybatis的全局配置文件中(mybatis‐config.xml):
<mappers>
<mapper resource="CommonSql.xml"/>
……
</mappers>
【3】引用SQL片段
最后在需要使用该sql片段的地方通过include标签的refId属性引用该sql片段:
<include refId=”名称空间.sql片段的id” />在UserMapper.xml的映射文件中,
进一步改造根据用户名查询用户信息
<select id="findById" resultMap="userMap" parameterType="int">
select <include refid="CommonSql.userSql"/> from user where id = ${id}
</select>
四、动态SQL
MyBatis 的强大特性之一便是它的动态SQL。可以根据不同条件拼接 SQL 语句
在 MyBatis 之前的版本中,有很多元素需要花时间了解。
MyBatis 3 开始精简了元素种类,现在只需学习原来一半的元素便可。
MyBatis 采用功能强大的 OGNL 的表达式来淘汰其它大部分元素。
OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言, 通过它可以非常方便的来操作对象属性
动态SQL中的业务逻辑判断需要使用到以下运算符:
1、ognl表达式
1. e1 or e2 满足一个即可
2. e1 and e2 都得满足
3. e1 == e2,e1 eq e2 判断是否相等
4. e1 != e2,e1 neq e2 不相等
5. e1 lt e2:小于 lt表示less than
6. e1 lte e2:小于等于,其他gt(大于),gte(大于等于) gt 表示greater than
7. e1 in e2
8. e1 not in e2
9. e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
10. !e,not e:非,求反
11. e.method(args)调用对象方法
12. e.property对象属性值 user.userName
13. e1[ e2 ]按索引取值,List,数组和Map
14. @class@method(args)调用类的静态方法
15. @class@field调用类的静态字段值
2、Mybatis常见标签如下:
If标签:判断
<if test="判断条件">
满足条件sql加入拼接
</if>
说明:
1)if标签:判断语句,用于进行逻辑判断的。如果判断条件为true,则执行if标签的文本内容
2)test属性:用来编写表达式,支持ognl
/**
* 【需求】:查询男性用户,如果输入了用户名,
* 按用户名模糊查询,如果没有输入用户名,就查询所有男性用
*/
List<User> findUsersByUserName(@Param("userName") String userName);
<select id="findUsersByUserName" resultMap="userMap">
select * from user where sex='男'
<if test="userName!=null">
and user_name like concat('%',#{userName},'%')
</if>
</select>
@Test
public void test22(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
//select * from user where sex='男'
//List<User> users = mapper.findUsersByUserName(null);
//select * from user where sex='男' and concat('%',?,'%');
List<User> users = mapper.findUsersByUserName("唐僧");
System.out.println(users);
MybatisUtil.close();
}
choose 标签:分支判断
choose标签:(分支选择,多选一,遇到成立的条件即停止)
when子标签:(编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行)
test属性:(编写ognl表达式)
otherwise子标签:(当所有条件都不满足时,才会执行该条件。)
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
where标签
where标签:拼接多条件查询时
1、能够添加where关键字;
2、能够去除多余的and或者or关键字;
where多条件语法格式:
<!--举例-->
<select>
select * from t
<where>
<if test="条件">
字段 = #{值1}
</if>
<if test="条件">
AND 字段名 = #{值2}
</if>
</where>
</select>
<!--
where标签作用:
1)适当添加where关键字
2)被where标签包裹的sql会自动去除多余的and或者or关键字
-->
set标签
set标签:
在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。
语法格式:
<update>
update t
<set>
<if test='条件'>
字段名1=值1,
</if>
<if test='条件2'>
字段名2=值2,
</if>
</set>
where 条件
</update>
foreach:循环遍历标签
foreach标签作用:遍历集合或者数组
语法格式:
<foreach collection=“集合名或者数组名“ item=”集合中每一个元素" separator="标签分隔符"
open="以什么开始" close="以什么结束">
#{元素}
</foreach>
说明:
collection属性:接收的集合或者数组,集合名或者数组名
item属性:集合或者数组参数中的每一个元素
separator属性:标签分隔符
open属性:以什么开始
close属性:以什么结束
mybatis延迟加载【了解】
1、延迟加载概述
-
应用场景
如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。
-
延迟加载的好处
先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。
-
延迟加载的条件:
1)resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
2)延迟加载是针对分步查询而言的
2、开启延迟加载
Mybatis的延迟加载功能默认是关闭的
需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能
需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能开启延迟加载的属性:
lazyLoadingEnabled:全局性设置懒加载。默认为false,true表示开启延迟加载
aggressiveLazyLoading:false表示关闭积极加载
说明:这两个属性必须一起设置
【示例】
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--关闭积极加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
3、延迟加载测试
延迟加载需求:通过订单编号20140921003查询order并延迟加载user。就是演示上述演示过的一对一的表关系案例。
分析:
如果改成延迟加载,也就意味着,先查询order,等需要的时候再去查询user,那就相当于将上面的一条语句变成了两条语句:
1、通过订单编号查询order
2、通过查询出来的order中的user_id查询user
SQL语句:
分步查询:
#第一步:根据order_number查询订单信息;
SELECT * FROM tb_order WHERE order_number = '20140921003';
#第二步:根据订单信息中的user_id查询出下单人的信息;
SELECT * FROM tb_user WHERE id = 1;
第一步:编写接口方法及映射文件
1.在OrderMapper接口中新建:queryOrderUserLazy方法;
Order queryOrderUserLazy(String orderNumber);
2.编写映射文件,分部查询,通过association标签下的column和select属性获取参数并传递到子查询中;
<!--引入在查询order信息的同事需要将用户的信息一并封装到order实体下,需要进行高级映射-->
<resultMap id="orderUserLazyMap" type="order" autoMapping="true">
<!--主键映射-->
<id column="order_id" property="id"/>
<!--普通字段映射 order_number:orderNumber, 因为settting中设置了驼峰规则,所以可省略-->
<result column="order_number" property="orderNumber"/>
<!--用于映射user对象,关系:一对一,可使用association实现映射-->
<association property="user" javaType="user" column="user_id" select="com.heima.dao.UserMapper.queryById">
</association>
</resultMap>
<select id="queryOrderUserLazy" resultMap="orderUserLazyMap">
select * from tb_order where order_number=#{orderNumber}
</select>
第二步:开启懒加载
在mybatis-config.xml全局配置文件中,开启懒加载;
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--关闭积极加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
第三步:测试
@Test
public void queryOrderUserLazy(){
Order order = orderMapper.queryOrderUserLazy("20140921001");
System.out.println("此时没有进行用户信息查询");
System.out.println(order.getOrderNumber());
//当我们使用user对象时,才从数据库中加载
User user = order.getUser();
System.out.println(user.toString());
}
小结:
延迟加载的利与弊
好处:先从单表进行查询数据,需要时再从关联表去关联查询,将会提高数据库性能,因为查询单表要比关联查询多张表速度快。
坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
五、Mybatis高级查询【重点】
1、mybatis高级查询环境准备
配置UserMapper.xml映射文件和接口
1. 定义接口
public interface UserMapper {
User findByUserId(@Param("id") Long id);
}
2. 配置映射文件
<?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.mapper.UserMapper">
<select id="findByUserId" resultType="user">
select * from tb_user where id=#{id}
</select>
</mapper>
单元测试
public class TestAll2 {
@Test
public void test1(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
User user = mapper.findByUserId(1l);
System.out.println(user);
MybatisUtil.close();
}
}
2、一对一查询
语法格式:
<resultMap id="映射ID" type="主表实体名称" autoMapping="true" >
<!-- 添加主表语主表实体映射 -->
......
<!--association:配置关联对象(User)的映射关系,一般与resultMap标签联合使用 -->
<association property="主表实体中对应从表的属性名称" javaType="从表实体类型" autoMapping="true">
<!-- 添加从表中字段与实体属性映射关系 -->
</association>
</resultMap>
案例演示:
通过订单编号20140921003查询出订单信息,并查询出下单人信息
-- 方式1:分步查询
-- 1.1根据订单编号查询订单信息
select * from tb_order where order_number='20140921003';-- user_id:1
-- 1.2根据user_id=1查询下单人信息
select * from tb_user where id=1;
-- 方式2:关联查询
select tb_order.id as order_id,tb_order.order_number,tb_user.*
from tb_order,tb_user
where tb_user.id=tb_order.user_id
and tb_order.order_number='20140921003';
添加order接口及方法
public class Order {
private Integer id;
private String orderNumber;
private User orderUser;
//getter setter toString
}
创建order映射文件,编写SQL
<?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.mapper.OrderMapper">
---------------------------------------------------------------------
<!--自定义映射规则-->
<resultMap id="orderMap" type="Order" autoMapping="true">
<id column="order_id" property="id"/>
<result column="order_number" property="orderNumber"/>
<!-- association:表示一对一映射 property="orderUser":表示指定映射的属性名称 javaType="User":指定映射属性的类型 autoMapping="true":开启自动映射(查询字段名称与pojo类中属性名称一致,可以不写) -->
<association property="orderUser" javaType="User" autoMapping="true">
<id column="id" property="id"/>
<result column="user_name" property="userName"/> </association>
</resultMap>
---------------------------------------------------------------------
<select id="findOrderByOrderNumber" resultMap="orderMap"> select tb_order.id as order_id, tb_order.order_number, tb_user.* from tb_order, tb_user where tb_user.id = tb_order.user_id and tb_order.order_number = #{orderNumber}
</select>
</mapper>
测试
@Test
public void test2(){
OrderMapper mapper = MybatisUtil.getMapper(OrderMapper.class);
Order order = mapper.findOrderByOrderNumber("20140921003");
System.out.println(order);
MybatisUtil.close();
}
3、一对多查询
案例:查询id为1的用户及其订单信息
-- 方式1:分步查询
-- 1.1 根据id查询用户信息
select * from tb_user where id=1; -- id=1
-- 1.2 根据用户id查询订单集合
select * from tb_order where user_id=1;
-- 方式2:一步查询
select
tb_user.*,
tb_order.id as order_id,
tb_order.order_number
from tb_user , tb_order
where tb_user.id = tb_order.user_id
and tb_user.id = 1;
User实体添加映射关系
public class User implements Serializable{
private List<Order> orders;
private Long id;
private String userName; // 用户名
private String password; // 密码
private String name; // 姓名
private Integer age; // 年龄
private Integer sex; //0-女 1-男
// getter and setter and toString
}
编写接口
/**
* 根据用户id查询用户信息,包含订单集合信息
* @param id
* @return
*/
User findUserAndOrdersByUserId(@Param("id") Long id);
编写sql映射文件关联订单集合
<!--定义一对多的映射规则-->
<resultMap id="userMap" type="user" autoMapping="true">
<!--映射主表-->
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<!--配置一对多映射-->
<collection property="orders" javaType="list" ofType="order" autoMapping="true">
<id column="order_id" property="id"/>
<result column="order_number" property="orderNumber"/>
</collection>
</resultMap>
<select id="findUserAndOrdersByUserId" resultMap="userMap">
select
tb_user.*,
tb_order.id as order_id,
tb_order.order_number
from tb_user,tb_order
where tb_user.id = tb_order.user_id
and tb_user.id = #{id}
</select>
测试
@Test
public void test3(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
User user = mapper.findUserAndOrdersByUserId(1l);
System.out.println(user);
MybatisUtil.close();
}
4、多对多查询
案例:查询订单号为20140921001的订单的详情信息,包含订单信息以及订单中的商品信息
第1步:分析SQL实现思路
select
tor.id as order_id,
tor.order_number,
tord.id as detail_id,
tord.status,
tord.total_price,
ti.id as item_id,
ti.item_detail,
ti.item_name,
ti.item_price
from tb_order as tor,
tb_orderdetail as tord,
tb_item ti
where tor.id = tord.order_id
and ti.id = tord.item_id
and tor.order_number = '20140921001';
第2步:添加类属性关联关系
public class Order {
private Integer id;
private String orderNumber;
private User user;
private List<Orderdetail> orderdetailList;
...
}
一条订单详情记录中都包含了一条商品信息,需要在Orderdetail中添加一个Item属性
public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
private Item item;
......
}
第3步:编写接口方法
/**
* 根据订单编号查询订单信息和详情信息以及关联的商品信息
* @param orderNumber
* @return
*/
Order findByOrderNumber2(@Param("orderNumber") String orderNumber);
第4步:编写SQL映射文件
<!--定义多对多映射规则-->
<resultMap id="orderMap2" type="order" autoMapping="true">
<id column="order_id" property="id"/>
<result column="order_number" property="orderNumber"/>
<collection property="orderdetailList" javaType="list"
ofType="orderdetail" autoMapping="true">
<id column="detail_id" property="id"/>
<result column="total_price" property="totalPrice"/>
<association property="item" javaType="item" autoMapping="true">
<id column="id" property="id"/>
<result column="item_name" property="itemName"/>
</association>
</collection>
</resultMap>
<select id="findByOrderNumber2" resultMap="orderMap2">
..........
</select>
第5步:测试
@Test
public void test4(){
OrderMapper mapper = MybatisUtils.getMapper(OrderMapper.class);
Order order = mapper.findByOrderNumber2("20140921001");
System.out.println(order);
MybatisUtils.close();
}
综合案例:
查询订单号为20140921001的订单信息、订单所属用户信息、订单中的详细商品信息:
映射文件
<!--定义多对多映射规则-->
<resultMap id="orderMap3" type="order" autoMapping="true">
<id column="order_id" property="id"/>
<result column="order_number" property="orderNumber"/>
<association property="user" javaType="user" autoMapping="true">
<id column="user_id" property="id"/>
<result column="user_name" property="userName"/>
</association>
<collection property="orderdetailList" javaType="list" ofType="orderdetail"
autoMapping="true">
<id column="detail_id" property="id"/>
<result column="total_price" property="totalPrice"/>
<association property="item" javaType="item" autoMapping="true">
<id column="id" property="id"/>
<result column="item_name" property="itemName"/>
</association>
</collection>
</resultMap>
<select id="findByOrderNumber3" resultMap="orderMap3">
SELECT
tb_user.id as user_id,
tb_user.age,
tb_user.`password`,
tb_user.sex,
tb_user.user_name,
tb_user.`name`,
tb_order.id AS order_id,
tb_order.order_number,
tb_orderdetail.id AS detail_id,
tb_orderdetail.total_price,
tb_orderdetail.`status`,
tb_item.*
FROM
tb_order,
tb_orderdetail,
tb_item,
tb_user
WHERE
tb_order.id = tb_orderdetail.order_id
AND tb_orderdetail.item_id = tb_item.id
AND tb_order.user_id=tb_user.id
AND tb_order.order_number = #{orderNumber}
</select>
测试
@Testpublic void test5(){
OrderMapper mapper = MybatisUtils.getMapper(OrderMapper.class);
Order order = mapper.findByOrderNumber3("20140921001");
System.out.println(order);
MybatisUtils.close();
}
小结
一对一关联查询实现步骤:
1) 维护pojo对象之间一对一的关系:
Order 包含属性:User类型的变量
2) 配置一对一的映射关系:
<resultMap id="唯一标识" type="映射的类" autoMapping="true">
<id column="主键字段" property="映射类中属性名称"/>
<result column="非主键字段" property="映射类中属性名称"/>
<!‐‐一对一映射‐‐>
<association property="映射的类中属性名称" javaType="java类型" autoMapping="true">
<id column="主键字段" property="java类型中属性名称"/>
<result column="非主键字段" property="java类型中属性名称"/>
</association>
</resultMap>
一对多关联查询实现步骤:
1. 在一对多的场景中,一般主表中通过创建一个集合属性来包含从表的数据
如: user类包含List<Order> orders);
2. 如何使用collection标签建立对集合的关联映射?
<!--一对多映射-->
<resultMap id="唯一标识" type="映射的类" autoMapping="true">
<id column="主键字段" property="映射类中属性名称"/>
<result column="非主键字段" property="映射类中属性名称"/>
<!--一对一映射-->
<collection property="映射的类中属性名称" javaType="list" ofType="集合泛型" autoMapping="true">
<id column="主键字段" property="java类型中属性名称"/>
<result column="非主键字段" property="java类型中属性名称"/>
</collection>
</resultMap>
5、ResultMap继承extend
如果两个ResultMap结果集有重叠的部分,可以通过extend属性继承简化;
将上述扩展通过extends继承后简化映射文件配置:
<resultMap id="orderMap4" type="order" extends="orderMap2" autoMapping="true">
<association property="user" javaType="user" autoMapping="true">
<id column="user_id" property="id"/>
<result column="user_name" property="userName"/>
</association>
</resultMap>
注意:特殊字符处理
编写Mapper映射文件时,有时候需要使用到一些诸如: > , < 之类的特殊字符。
这些字符不能直接书写在xml文件中,需要我们对其处理。
处理方式:使用转义字符代替特殊字符。
转义字符 | sql符号 | 说明 |
---|---|---|
<; | < | 小于 |
>; | > | 大于 |
&; | & | 与 |
&apos; | ’ | 单引号 |
&qouot; | " | 双引号 |
举例:批量将id小于3的用户性别改为男,在映射文件中直接写<号,xml约束会报错!
<select id="findUsersLt" resultMap="userMap">
select * from user where id < #{id}
</select>