视频:黑马程序员
参考文档: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 对象)为数据库中的记录
ORM思想
-
ORM是指(Object Relationship Mapping)对象关系映射
-
其中
- 对象:Java的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
-
体现
Java概念 数据库概念 类 表 属性 字段/列 对象 记录/行
环境搭建
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;
1.创建maven项目
2.在pom.xml文件中导入依赖
<dependencies>
<!--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>
</dependencies>
3.创建核心配置文件
<?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 resource="db.properties"/>
<!--
2.经典命名
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--
3.别名 只需要使用实体类类名User
-->
<typeAliases>
<package name="com.itheima.sh.pojo"/>
</typeAliases>
<!--
开发环境配置
-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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"/>
</mappers>
</configuration>
4.创建数据库配置文件db.properties
# 连接数据库的驱动
jdbc.driver=com.mysql.jdbc.Driver
# 连接数据库的连接地址
jdbc.url=jdbc:mysql:///mybatis01
# 连接数据库的用户名
jdbc.username=root
# 连接数据库的密码
jdbc.password=123456
5.映射文件
<?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.itheima.sh.dao.UserMapper">
<select id="queryAllUsers" resultType="User">
select * from user
</select>
</mapper>
6.创建UserMapper接口
package com.itheima.sh.dao;
public interface UserMapper {
List<User> queryAllUsers();
}
7.创建实体类User
package com.itheima.sh.pojo;
import java.util.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 + '\'' +
'}';
}
}
8.测试类
package com.itheima.sh.a_test_01;
import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.pojo.User;
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 org.junit.Test;
import java.util.List;
public class MyBatisTest {
@Test
public void queryAllUsers() throws Exception{
//1.创建会话工厂创造类的对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//2.创建会话工厂对象
SqlSessionFactory factory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));
//3.获取会话对象
SqlSession sqlSession = factory.openSession();
//4.使用sqlSession调用方法获取接口代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//5.使用接口代理对象调用方法
List<User> list = mapper.queryAllUsers();
//6.输出
System.out.println("list = " + list);
}
}
9.项目结构
篇章二:核心配置文件和映射文件
核心配置文件
核心配置文件是MyBatis的全局配置文件,包含全局配置信息,如数据库连接参数、插件等。整个框架中只需要一个即可,建议命名为mybatis-config.xml,不做强制要求。
1、mybatis全局配置文件是mybatis框架的核心配置,整个框架只需一个;
2、mybatis全局配置文件中的配置顺序:注意如果配置多项,必须按照以下顺序进行配置
properties:属性配置
settings:设置
typeAliases:类型别名设置
typeHandlers:类型处理器
enviroments:环境配置
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
mappers:映射器
1.properties标签
作用:
- 加载外部的java资源文件(properties文件);
- 通过子标签property设置属性;
范例:
<properties resource="db.properties"></properties>
2.settings标签
settings参数有很多
mapUnderscoreToCamelCase属性
开启驼峰匹配:完成经典的数据库命名到java属性的映射
经典数据库命名:如果多个单词之间,通常使用下划线进行连接。
java中命名:第二个单词首字母大写。
驼峰匹配:相当于去掉数据库的数据中的名字的下划线,和java进行匹配
应用场景:
从数据库中查到数据的字段名user_name和实体类属性名userName,不一致,导致查到的数据无法封装到实体类中。
但是,实体类中的属性userName符合驼峰命名,数据库字段名user_name符合数据库经典字段命名。
范例:
<!--
2、settings设置:
mapUnderscoreToCamelCase :驼峰自动映射配置 满足条件:数据库字段名 user_name ,实体类属性名 userName
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
解决字段名和属性名不一致的方法
如果数据库字段名和属性名不一致,或者是也不满足上面的命名规则。可以通过在SQL语句中为字段名取别名进行映射。
3.typeAliases(类型别名)
作用:类型别名是给类的全类名(包名.类名) 取一个短名称。存在的意义仅在于用来减少类完全限定名的冗余
范例:
<!-- 方式:使用typeAliases标签的子标签package包扫描映射别名;-->
<!-- package扫描指定包下的所有类,扫描之后的别名就是类名,大小写不敏感(不区分大小写),建议使用的时候和类名一致。-->
<typeAliases>
<package name="com.itheima.sh.pojo"></package>
</typeAliases>
<!--typeAliases还有一个子标签typeAlias 单独取别名
<typeAlias type="com.heima.mybatis.pojo.User" alias="User"/>
-->
【内置别名】
MyBatis中内建了一些类型的别名,常见的有。它们都是不区分大小写的,注意对基本类型名称重复采取的特殊命名风格。
别名 | 映射的类型 |
---|---|
_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 |
4.environments标签
MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离。
父标签: environments(环境配置)
子标签:
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
- 在environments标签中配置多个environment,通过属性default指定一个默认环境配置;
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
5.mapper标签
mappers(映射器):UserMapper.xml====>UserMapper.java接口 关联.
作用:维护接口和映射文件之间的关系.
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。 但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:///
的 URL),或类名和包名等。方式如下:
1、加载映射文件,关联UserMapper.java接口
<mapper resource="org/mybatis/example/BlogMapper.xml"/> 从src下加载映射文件;注意使用的是"/"
2、加载接口,关联映射文件
条件:1、接口名和映射文件名保持一致;2、路径保持一致;
【2】批量加载class:<package name="com.heima.mybatis.dao"/>
映射文件
Mapper映射文件中定义了操作数据库的sql,每一个sql都被包含在一个statement中。映射文件是mybatis操作数据库的核心。
在映射文件中我们需要研究的内容有: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="Mapper接口的全类名">
<select id="Mapper接口的方法名" resultType="接口方法的返回值类型">
select * from Blog where id = #{id}
</select>
</mapper>
-
映射配置文件要保证两个一致
- mapper接口的全类名和映射文件的命名空间namespace保持一致
- mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
-
<insert>标签
-
用于书写插入数据的SQL语句
-
id属性指定对应mapper接口的方法名
-
范例
<!-- 注意:#{username},#{birthday},#{sex},#{address} 大括号里面的值必须和pojo的实体类User类中的属性名一致,否则会报错。其实这里看的是User类中的getXxx()的get后面的xxx内容 例如 getUserName---看的是userName ,就是将get去掉,U变为小写u,后面不变 --> <insert id="saveUser"> insert into user values (null ,#{username},#{birthday},#{sex},#{address}) </insert>
-
-
<delete>标签
-
用于删除表中的数据
-
id属性指定对应mapper接口的方法名
-
范例
<!--删除--> <delete id="deleteUser" > delete from user where id = #{id} </delete>
-
-
<update>标签
-
用于更新表中数据
-
id属性指定对应mapper接口中的方法名
-
范例
<update id="updateUser"> SQL语句 </update>
-
-
<select>标签
-
用于查询表中的数据
-
id属性指定mapper接口中对应的方法名
-
resultType属性表示自动映射,用于属性名和表中字段名一致的情况
-
resultMap属性表示自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
-
范例
查询一条数据:
<!--根据id查询用户数据--> <!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名--> <select id="queryById" resultType="user" parameterType="int"> select * from user where id = #{id} </select>
查询多条数据到List集合:
<select id="getUserList" resultType="com.lxq.pojo.User"> SQL语句 </select>
-
篇章三:基于Mapper代理和注解开发
参数值的获取#{}和${}
获取接口方法中传入的参数。获取参数,有两种方式:#{}和${};
1、SQL语句中获取参数的方式:
#{xxx} sql:select * from user where id = ?
${xxx} sql:select * from user where id = 1
2、取值的相同点:
都能够获取接口方法传入的参数值
3、取值的不同点:
#{}取值:是以预编译的形式将参数设置到SQL语句中。PreparedStatement 防止SQL注入;
${}取值:直接把获取到的参数值,拼接到sql语句中,会有安全问题;不能防止SQL注入;
4、小结:
SQL传入参数的获取使用#{};
拼接参数使用${};
Mapper代理
1.传入的参数
CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型。入参数据有以下几种类型:HashMap,基本数据类型(包装类),实体类;
设置传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数类型。
简单数据类型:int、string、long、Date
-
单个参数:接口方法传入一个参数
当接口方法传入一个参数时,mybatis不做特殊处理,只需要#{任意变量名}都可接收;但是尽量见名知意
-
多个参数:接口方法传入多个参数
当传入多个参数时,mybatis底层进行了处理
-
使用参数索引获取:arg0,arg1
<!--根据用户名和性别查询--> <select id="queryByUserNameAndSex" resultType="User"> select * from user where username=#{arg0} and sex=#{arg1} </select>
-
使用参数位置获取:param1,param2
<!--根据用户名和性别查询--> <select id="queryByUserNameAndSex" resultType="User"> select * from user where username=#{param1} and sex=#{param2} </select>
-
使用命名参数获取,明确指定传入参数的名称:(建议使用这个)
//步骤一:在接口中传入参数时通过@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>
-
复杂数据类型:pojo实体类、Map集合
-
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); }
-
HashMap参数
需求:模拟用户登录,登录方法参数是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); }
2.结果映射
在使用原生的JDBC操作时,对于结果集ResultSet,需要手动处理。mybatis框架提供了resultType和resultMap来对结果集进行封装。
即:只要一个方法有返回值需要处理,那么 resultType和resultMap必须有一个
resultType
从sql语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
- 返回值是简单类型
例如 int ,string ===>resultType=“书写对应的基本类型别名或者全名即可”
-
返回值为一个pojo(User)对象时
【定义resultType为User】
【使用User来接收返回值】
-
返回值是一个List时
当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型:
【映射文件】
【接口】
【测试类】
//1.使用接口对象调用接口中的方法
List<User> userList = mapper.findAllUsers();
//2.遍历集合
for (User user : userList) {
System.out.println(user);
}
-
返回值是Map时
-
返回一条数据,封装到map中
需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中
接口方法:
//需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中 Map<String,Object> selectByIdReturnMap(Integer id);
SQL语句:
<select id="selectByIdReturnMap" resultType="map"> select * from user where id=#{id} </select>
测试:原来封装到对象中的数据也能够封装到map中
@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:
-
返回多条数据,封装到map中
需求:查询数据表所有的数据封装到Map<String,User>集合中
要求: Key值为一条记录的主键,Value值为pojo的对象.
如下所示:
接口方法:接口方法上面通过注解 @MapKey指定key值封装的列数据
//需求:查询数据表所有的数据封装到Map<String,User>集合中 @MapKey("id") 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();
resultMap
ResultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决两大问题:
- pojo属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式,比如id和userId)
- 完成高级查询,比如说,一对一、一对多、多对多。
【问题:】查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,而POJO中的属性名字是userName
两端不一致,造成mybatis无法填充对应的字段信息。
解决方案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自定义映射
<!--
type="user" 表示结果集的封装类型是user
-->
<resultMap id="userResultMap" type="User" autoMapping="true">
<!--配置主键映射关系-->
<id column="id" property="id"></id>
<!--配置用户名的映射关系 column 表示数据表列 property表示pojo的属性-->
<result column="name" property="username"></result>
</resultMap>
<!--
resultMap属性:引用自定义结果集作为数据的封装方式.属性值是自定义resultMap标签的id属性值,这里表示通过id引入自定义的resultMap标签
-->
<select id="queryById" resultMap="userResultMap">
select * from user where id = #{id}
</select>
动态SQL
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
动态SQL中的业务逻辑判断需要使用到以下运算符: 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对象属性值
13. e1[ e2 ]按索引取值,List,数组和Map
14. @class@method(args)调用类的静态方法
15. @class@field调用类的静态字段值
-
if标签
-
if标签通过test属性给出判断的条件,如果条件成立,则将执行标签内的SQL语句
-
范例
<select id="getEmpByCondition" resultType="Emp"> select * from t_emp where <if test="empName != null and empName != ''"> emp_name = #{empName} </if> <if test="age != null and age != ''"> and age = #{age} </if> <if test="gender != null and gender != ''"> and gender = #{gender} </if> </select>
-
-
where标签
-
考虑if标签中的范例出现的一种情况:当第一个if标签条件不成立而第二个条件成立时,拼接成的SQL语句中where后面连着的是and,会造成SQL语句语法错误,而where标签可以解决这个问题
-
范例
<select id="getEmpByCondition" resultType="Emp"> select * from t_emp <where> <if test="empName != null and empName != ''"> emp_name = #{empName} </if> <if test="age != null and age != ''"> and age = #{age} </if> <if test="gender != null and gender != ''"> and gender = #{gender} </if> </where> </select>
-
where标签只会在子标签返回任何内容的情况下才插入WHERE子句。而且,若子句的开头有多余的and或者or,where标签也会将它们去除,但是子句末尾的and或者or不能去除
-
-
trim标签
-
trim标签用于去掉或添加标签中的内容
-
trim标签常用属性
- prefix:在trim标签中的内容的前面添加某些内容
- prefixOverrides:在trim标签中的内容的前面去掉某些内容
- suffix:在trim标签中的内容的后面添加某些内容
- suffixOverrides:在trim标签中的内容的后面去掉某些内容
-
用trim实现where标签范例相同的功能
<select id="getEmpByCondition" resultType="Emp"> select * from t_emp <trim prefix="where" prefixOverrides="and"> <if test="empName != null and empName != ''"> emp_name = #{empName} </if> <if test="age != null and age != ''"> and age = #{age} </if> <if test="gender != null and gender != ''"> and gender = #{gender} </if> </trim> </select>
-
-
choose、when、otherwise标签
-
这三个标签是组合使用的,用于在多条件中选择一个条件,类似Java中的if…else if…else…语句
-
范例
<select id="getEmpByCondition" resultType="Emp"> select * from t_emp where gender = #{gender} <choose> <when test="empName != null and empName != ''"> and emp_name = #{empName} </when> <when test="age != null and age != ''"> and age = #{age} </when> </choose> </select>
-
当某个when标签的条件满足时将对应的SQL语句返回,如果都不满足并且有otherwise标签时,才会返回otherwise标签中的SQL语句
-
-
foreach标签
-
foreach标签允许指定一个集合或数组,并且对这个集合或数组进行遍历
-
foreach标签可以用的属性有
- collection:指定需要遍历的集合或数组
- item:当前遍历到的元素
- index:当前遍历到的元素的序号
- 当遍历的集合是Map类型时,index表示键,item表示值
- open:指定遍历开始前添加的字符串
- close:指定遍历开始后添加的字符串
- separator:指定每次遍历之间的分隔符
-
collection属性值注意事项
- 如果遍历的是List时,属性值为list
- 如果遍历的是数组时,属性值为array
- 如果遍历的是Map时,属性值可以是map.keys()、map.values()、map.entrySet()
- 除此之外,还可以在映射方法的参数中使用**@Param()**注解自定义collection属性值
-
批量添加数据
<insert id="addMoreEmp"> insert into t_emp values <foreach collection="list" separator="," item="emp"> (null,#{emp.empName},#{emp.age},#{emp.gender},null) </foreach> </insert>
-
批量删除数据
<delete id="deleteMoreEmp"> delete from t_emp where emp_id in <foreach collection="array" item="empId" separator="," open="(" close=")"> #{empId} </foreach> </delete>
-
-
sql标签
-
用于记录一段通用的SQL语句片段,在需要用到该SQL语句片段的地方中通过include标签将该SQL语句片段插入
-
sql标签通过id属性唯一标识一个SQL语句片段,include标签通过refid属性指定使用某个SQL片段
-
范例
<sql id="item"> emp_id,emp_name,age,gender,dept_id </sql> <select id="getEmpByEmpId" resultType="Emp"> select <include refid="item"></include> from t_emp where emp_id = #{empId} </select>
-
- set标签
set标签:在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。
案例:修改用户信息,如果参数user中的某个属性为null,则不修改。
如果在正常编写更新语句时,如下:
update user SET username = ?, birthday=?, sex=?, where id = ?
# 那么一旦在传递的参数中没有address,此时生成的sql语句就会因为多了一个逗号而报错。
<!--* 修改用户信息,如果参数user中的某个属性为null,则不修改-->
<!--void updateUserById(User user);-->
<update id="updateUserById">
update user
<set>
<if test="userName!=null">
user_name=#{userName},
</if>
<if test="birthday!=null">
birthday=#{birthday},
</if>
<if test="sex!=null">
sex=#{sex},
</if>
<if test="address!=null">
address=#{address}
</if>
</set>
where id=#{id}
</update>
自增主键回填
新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值。
-
使用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。
多表查询
多表查询使用resultMap标签完成
步骤:
1、首先,编写接口方法。编写SQL语句;
2、第二步:分析SQL,封装数据(关联对象);
3、处理多表之间的数据封装(数据库字段名—》实体类的属性名之间的映射)
resultMap:
属性:
id 唯一标识,被引用的时候,进行指定
type 结果集对应的数据类型 Order
autoMapping 开启自动映射
extends 继承
子标签:
id:配置id属性
result:配置其他属性
association:配置一对一的映射
property 定义对象的属性名
javaType 属性的类型
autoMapping 开启自动映射
collection:配置一对多的映射
property 定义对象的属性名
javaType 集合的类型
ofType 集合中的元素类型 泛型
autoMapping 开启自动映射
一对一查询
一对一关联查询:
1、需要在Order实体类中关联User对象;最终将数据封装到Order中;
2、在OrderMapper.xml文件中书写关联语句并配置关系;
3、关联关系配置:
<resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
<!--主表主键-->
<id column="oid" property="id"/>
<!--关联关系-->
<association property="user" javaType="User" autoMapping="true">
<!--从表主键-->
<id column="uid" property="id"/>
</association>
</resultMap>
一对多查询
一对多关系配置:
1、在对象中添加映射关系;
2、编写接口方法,编写SQL;
3、编写resultMap处理数据库字段和实体类之间数据的封装;
<resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
<!--主表主键-->
<id column="oid" property="id"/>
<!--关联关系-->
<collection property="user" javaType="User" autoMapping="true">
<!--从表主键-->
<id column="uid" property="id"/>
</collection>
</resultMap>
基于注解
上述我们已经学习mybatis的SQL映射文件可以使用xml的方式配置,但是我们发现不同的用户模块接口都对应一个映射文件,并且在映射文件中书写sql语句也比较麻烦。所以Mybatis为用户提供了快速的开发方式,因为有时候大量的XML配置文件的编写时非常繁琐的,因此Mybatis也提供了更加简便的基于注解(Annnotation)的配置方式。
注解实现CRUD
@Insert:保存
Value:sql语句(和xml的配置方式一模一样)
@Update:更新
Value:sql语句
@Delete: 删除
Value:sql语句
@Select: 查询
Value:sql语句
@Options:可选配置(获取主键)
userGeneratedKeys:开关,值为true表示可以获取主键 相当于select last_insert_id()
keyProperty :对象属性
keyColumn : 列名
insert:
public interface UserMapper {
//1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
@Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
void saveUser(User user);
}
delete:
/*
2.根据id删除用户
*/
@Delete("delete from user where id=#{id}")
void deleteUserById(Long id);
update:
/**
* 3.修改用户数据
* @param user
*/
@Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}")
void updateUser(User user);
query:
/**
* 3.修改用户数据
* @param user
*/
@Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}")
void updateUser(User user);
自增主键回填
1、在新增数据注解 @Insert下面,添加@Options;
2、在Options注解中,设置useGeneratedKeys值为true,keyProperty为id,keyColumn id;
//1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
@Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
@Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
void saveUser(User user);
@Options原码
动态SQL
方式1:@SelectProvider
使用 @SelectProvider 注解,注解中的type 参数是提供构建 SQL 的类,method 是构建 SQL 的方法。构建 SQL 的方法的参数要和接口的参数一致,并且多个参数要使用命名参数。
接口:
@SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL")
List<User> queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);
动态sql生成类
package com.itheima.sh.utils;
import org.apache.ibatis.annotations.Param;
public class ProviderUtils {
public String queryUsersBySexOrUsernameSQL(@Param("sex") String sex, @Param("username") String username){
String sql = "select * from tb_user where sex = #{sex}";
if (username!=null && !"".equals(username)){
sql += " and user_name like concat('%',#{username},'%')";
}
return sql;
}
}
方式2:@SelectProvider和SQL类
为了防止没有必要的空格引起的错误,mybatis提供了一个SQL类来实现sql语句拼接,在SQL类中将sql语句的关键字都变为方法名,并且每个方法返回值都是SQL类型,可以链式编程.
借助mybatis提供的一个对象:SQL。
public class ProviderUtils {
public String queryUsersBySexOrUsernameSQL2(@Param("sex") String sex, @Param("username") String username) {
//借助mybatis提供的一个对象:SQL
//创建SQL对象
SQL sql = new SQL();
//链式编程,每个方法返回值都是SQL类对象
sql.SELECT("*").FROM("tb_user").WHERE("sex = #{sex}");
//判断用户是否为空,不为空就继续链式编程,即继续拼接
if(username != null && !"".equals(username)){
sql.AND().WHERE("user_name like concat('%',#{username},'%')");
}
//SELECT * FROM user WHERE (sex=? AND user_name like concat('%',?,'%'))
//转换为字符串并返回
return sql.toString();
}
}