环境:
- JDK 1.8
- Mysql 5.7(Mysql 8.0)
- maven 3.6.1
- IDEA
回顾:
- JDBC
- Mysql
- Java基础
- Maven
- Junit
SSM框架:配置文件的。最好的方式:看官方文档
1、简介
Mybatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录
Mybatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了Google Code,并且改名为Mybatis。2013年11月迁移到Github
如何获得Mybatis
- Maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
- Github:
https://github.com/mybatis/mybatis-3/releases
- 中文文档:
https://mybatis.org/mybatis-3/zh/index.html
持久化
- 数据持久化:持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 瞬时状态:类似于内存,断电即失
- 持久状态:只要不删库,数据可以一直在
- 数据库(JDBC),IO文件持久化
- 生活:冷藏、罐头、…
- 持久化的原因:有一些对象,不能让他丢掉(类似生活中的微信、支付宝余额,王者的点卷,…),主要原因:内存太贵了
持久层
- Dao层、Service层、Controller层
- 完成持久化工作的代码块
- 层是界限十分明显
为什么需要Mybatis
- 方便
- 传统的JDBC代码太复杂了;因为简化,所以框架,自动化
- 帮助程序猿将数据存入到数据库中
- 不用Mybatis也可以。更容易上手
优点:
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供映射标签,支持对象与数据库的orm字段关系映射。
- 提供对象关系映射标签,支持对象关系组建维护。
- 提供xml标签,支持编写动态sql。
最重要的一点:使用的人多!!!
2、第一个Mybatis程序
思路:搭建环境 -> 导入Mybatis -> 编写代码 -> 测试
搭建环境
- 搭建数据库
- 建库:
CREATE DATABASE 'mybatis'; USE 'mybatis';
- 建表:
CREATE TABLE 'user'( 'id' INT(20) NOT NULL PRIMARY KEY, 'name' VARCHAR(30) DEFAULT NULL, 'pwd' VARCHAR(30) DEFAULT NULL )ENGINE=INNODB DEFAULT CHARSET=utf8;
- 插入数据:
INSERT INTO 'user' ('id', 'name', 'pwd') VALUES (1, 'root', '123456'), (2, 'huake', '123'), (3, 'zya', '123456')
- 建库:
数据库的几种引擎:
- MyISAM:默认的MySQL插件式存储引擎,它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。注意,通过更改STORAGE_ENGINE配置变量,能够方便地更改MySQL服务器的默认存储引擎。
- InnoDB:用于事务处理应用程序,具有众多特性,包括ACID事务支持。(提供行级锁)
- BDB:可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性。
- Memory:将所有数据保存在RAM中,在需要快速查找引用和其他类似数据的环境下,可提供极快的访问。
- Merge:允许MySQL DBA或开发人员将一系列等同的MyISAM表以逻辑方式组合在一起,并作为1个对象引用它们。对于诸如数据仓储等VLDB环境十分适合。
- Archive:为大量很少引用的历史、归档、或安全审计信息的存储和检索提供了完美的解决方案。
- Federated:能够将多个分离的MySQL服务器链接起来,从多个物理服务器创建一个逻辑数据库。十分适合于分布式环境或数据集市环境。
- Cluster/NDB:MySQL的簇式数据库引擎,尤其适合于具有高性能查找要求的应用程序,这类查找需求还要求具有最高的正常工作时间和可用性。
- Other:其他存储引擎包括CSV(引用由逗号隔开的用作数据库表的文件),Blackhole(用于临时禁止对数据库的应用程序输入),以及Example引擎(可为快速创建定制的插件式存储引擎提供帮助)。
一般来说不使用事务的话,请使用MyISAM引擎,使用事务的话,一般使用InnoDB
新建项目:
- 1、新建一个普通的maven项目
- 2、删除src目录
- 3、导入maven依赖
<!-- 父工程 --> <groupId>org.example</groupId> <artifactId>Mybatis_demo</artifactId> <version>1.0-SNAPSHOT</version> <!-- 导入依赖 --> <dependencies> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- mybatis --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> </dependencies>
创建一个模块:
-
编写mybatis的核心配置文件
<?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: 核心配置文件 --> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="zya11230318"/> </dataSource> </environment> </environments> </configuration>
-
编写mybatis的工具类
package com.zya.pojo; import lombok.AllArgsConstructor; import lombok.*; import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @AllArgsConstructor @NoArgsConstructor // 实体类 @Data public class User { private int id; private String name; private String pwd; }
-
Dao接口
package com.zya.dao; import com.zya.pojo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
-
接口实现类由原来的UserDaoImpl转变为一个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"> <!-- namespace: 绑定一个对应的Dao/Mapper接口 --> <mapper namespace="com.zya.dao.UserDao"> <!-- select: 查询语句 --> <!-- 绑定接口方法 --> <select id="getUserList" resultType="com.zya.pojo.User"> select * from mybatis.userone; </select> </mapper>
-
pom.xml
<!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
测试
注意点:org.apache.ibatis.binding.BindingException: Type interface com.zya.dao.UserDao is not known to the MapperRegistry.
import com.zya.dao.UserDao;
import com.zya.pojo.User;
import com.zya.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyTest {
@Test
public void test(){
// 第一步: 获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 方式一: getMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
// 方式二 不推荐使用
// List<User> userList = sqlSession.selectList("com.zya.dao.UserDao.getUserList");
for (User user:userList) {
System.out.println(user);
}
// 关闭SqlSession
sqlSession.close();
}
}
MapperRegistry是什么?
- 核心配置文件中注册mappers
- junit测试
可能遇到的问题:
- 1、配置文件没有注册
- 2、绑定接口错误
- 3、方法名不对
- 4、返回类型不对
- 5、Maven导出资源问题
3、CRUD
1、namespace
- namespace中的包名要和Dao/Mapper接口的包名一致
- 选择、查询语句
- id:就是对应的namespace中的方法名
- resultType:Sql语句执行的返回值!
- parameterType:参数类型
2、select、insert、update、delete
-
编写接口
package com.zya.dao; import com.zya.pojo.User; import java.util.List; public interface UserMapper { // 查询全部用户 List<User> getUserList(); // 根据id查询用户 User getUserById(int id); // 插入用户 int addUser(User user); // 修改用户 int updateUser(User user); // 删除一个用户 int deleteUser(int id); }
-
编写对应的mapper中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"> <!-- namespace: 绑定一个对应的Dao/Mapper接口 --> <mapper namespace="com.zya.dao.UserMapper"> <!-- select: 查询语句 --> <!-- 绑定接口方法 --> <select id="getUserList" resultType="com.zya.pojo.User"> select * from mybatis.userone; </select> <select id="getUserById" resultType="com.zya.pojo.User" parameterType="int"> select * from mybatis.userone where id=#{id}; </select> <!-- 对象中的属性, 可以直接取出来 --> <insert id="addUser" parameterType="com.zya.pojo.User"> insert into mybatis.userone (id, name, pwd) values (#{id}, #{name}, #{pwd}) </insert> <update id="updateUser" parameterType="com.zya.pojo.User"> update mybatis.userone set name=#{name},pwd=#{pwd} where id=#{id}; </update> <delete id="deleteUser" parameterType="int"> delete from mybatis.userone where id=#{id}; </delete> </mapper>
-
测试
import com.zya.dao.UserMapper; import com.zya.pojo.User; import com.zya.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class MyTest { @Test public void test(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 方式一: getMapper List<User> userList = sqlSession.getMapper(UserMapper.class).getUserList(); // 方式二: // List<User> userList = sqlSession.selectList("com.zya.dao.UserMapper.getUserList"); for (User user:userList) { System.out.println(user); } }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } @Test public void test01(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 方式一: getMapper User user = sqlSession.getMapper(UserMapper.class).getUserById(2); System.out.println(user); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } // 增删改需要提交事务 @Test public void test02(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 方式一: getMapper int res = sqlSession.getMapper(UserMapper.class).addUser(new User(5, "admin", "123456")); // 提价事务 sqlSession.commit(); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } @Test public void test03(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 方式一: getMapper int res = sqlSession.getMapper(UserMapper.class).updateUser(new User(5, "admin", "123")); // 提价事务 sqlSession.commit(); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } @Test public void test04(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 方式一: getMapper int res = sqlSession.getMapper(UserMapper.class).deleteUser(5); // 提价事务 sqlSession.commit(); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } }
注意点:增删改需要提交事务!!!
分析错误
- 标签不要匹配错
- resource绑定mapper,需要使用路径
- 程序配置文件必须符合规范!
- NullPointException,没有注册到资源!
- 输出的xml文件中存在中文乱码问题
- maven资源没有导出问题
万能Map
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map
dao接口
public interface UserMapper {
// 万能的Map
int addUser2(Map<String, Object> map);
}
UserMapper.xml
<!--
对象中的属性, 可以直接取出来
传递map中的key
-->
<insert id="addUser2" parameterType="map">
insert into mybatis.userone (id, name, pwd) values (#{userId}, #{userName}, #{userPwd});
</insert>
Test.java
@Test
public void test05(){
// 第一步: 获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
Map<String, Object> map = new HashMap<String, Object>();
map.put("userId", 5);
map.put("userName", "admin");
map.put("userPwd", "123456");
// 方式一: getMapper
int res = sqlSession.getMapper(UserMapper.class).addUser2(map);
// 提价事务
sqlSession.commit();
}catch (Exception e){
e.printStackTrace();
}finally {
// 关闭SqlSession
sqlSession.close();
}
}
- Map传递参数,直接在sql中取出key即可【parameterType=“map”】
- 对象传递参数,直接在sql中取对象的属性即可【parameterType=“object”】
- 只有一个基本类型参数的情况下,可以直接在sql中取到
- 多个参数用Map,或者注解
模糊查询:
-
1、Java代码执行的时候,传递通配符 % %
List<User> user = sqlSession.getMapper(UserMapper.class).getUserLike("%w%");
-
2、在sql拼接中使用通配符
select * from mybatis.userone where name like "%"#{value}"%";
4、配置解析
1、核心配置文件
- mybatis-config.xml
- MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
2、环境配置(environments)
- MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
- 不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
注意一些关键点:
- 默认使用的环境 ID(比如:default=“development”)。
- 每个 environment 元素定义的环境 ID(比如:id=“development”)。
- 事务管理器的配置(比如:type=“JDBC”)。
- 数据源的配置(比如:type=“POOLED”)。
事务管理器(transactionManager):
- 在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。(Mybatis默认的事务管理器是JDBC)
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE
应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为
false 来阻止默认的关闭行为。
数据源(dataSource):(Mybatis默认的连接池是POOLED)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
- 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
- UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
- driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)
- url – 这是数据库的 JDBC URL 地址。
- username – 登录数据库的用户名。
- password – 登录数据库的密码。
- defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
- defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:driver.encoding=UTF8
,这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8 的 encoding 属性给数据库驱动。
- POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
- poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
- poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
- poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000
毫秒(即 20 秒) - poolTimeToWait –这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000毫秒(即 20 秒)。
- poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置,作用于每一个尝试从缓存池获取连接的线程。如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance之和。 默认值:3(新增于 3.4.5)
- poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERYSET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
- poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL语句(最好是一个速度非常快的 SQL 语句),默认值:false。
- poolPingConnectionsNotUsedFor – 配置 poolPingQuery
的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
- JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
- initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
- data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:env.encoding=UTF8
,这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8 的 encoding 属性。
3、属性(properties)
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties】
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
- 可以直接引入外部配置文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件的
编写一个数据库配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=GMT%2B8
username=root
password=zya11230318
在xml中,所有的标签都可以规定其顺序
4、类型别名(typeAliases)
- 类型别名可为 Java 类型设置一个缩写名字。
- 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<!-- 可以给实体类起别名 -->
<typeAliases>
<typeAlias type="com.zya.pojo.User" alias="User" />
</typeAliases>
也可以指定一个包名,MyBatis会在包名下面搜索需要的Java Bean,比如
- 扫描实体类的包,它的默认别名就为这个类的类名,首字母小写
<!-- 可以给实体类起别名 -->
<typeAliases>
<typeAlias type="com.zya.pojo.User" />
</typeAliases>
在实体类比较少的时候,使用第一种方式【可以DIY】
如果实体类十分多,建议使用第二种【不可DIY,如果非要改,需要在实体类上增加注解】
@Alias("User")
public class User {
...
}
5、设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
6、其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- mybatis-plus
- mybatis-generator-core
- 通用mapper
7、映射器(mappers)
MapperRegistry:注册绑定我们的Mapper文件
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等
- 方式一:
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
- 方式二:
<!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>
- 方式三:使用class绑定注册(注意点如下)
- 接口和它的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
<!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
- 方式四:使用扫描包进行注入绑定(注意点如下)
- 接口和它的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
<!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
8、生命周期和作用域
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
- 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了
- 局部变量
SqlSessionFactory:
- 说白了就是:数据库连接池
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。(会浪费内存资源)
- 最简单的就是使用单例模式或者静态单例模式
SqlSession:
- 每个线程都应该有它自己的 SqlSession 实例(连接到连接池的一个请求)
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后需要赶紧关闭,否则资源被占用(每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它)
这里面的每一个Mapper,就代表一个具体的业务!
5、解决属性名和字段名不一致的问题
数据库中的字段
新建一个项目,拷贝之前的,测试实体类字段不一致的情况
package com.zya.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.type.Alias;
@AllArgsConstructor
@NoArgsConstructor
// 实体类
@Alias("User")
@Data
public class User {
private int id;
private String name;
private String password;
}
测试出现问题,找不到pwd
select id, name, pwd from mybatis.userone;
解决方法:
- 起别名【select id, name, pwd as password from mybatis.userone;】
- resultMap:结果集映射
resultMap 元素是 MyBatis 中最重要最强大的元素
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了
没有一个需要显式配置 ResultMap,这就是 ResultMap 的优秀之处——你完全可以不用显式地配置它们
如果这个世界总是这么简单就好了
id, name, pwd 数据库字段
id, name, password 实体类
<!-- 结果集映射 -->
<resultMap id="UserMap" type="User">
<!-- column: 数据库中的字段 -->
<!-- property: 实体类中的字段 -->
<result column="id" property="id" />
<result column="name" property="name" />
<result column="pwd" property="password" />
</resultMap>
<!-- select: 查询语句 -->
<!-- 绑定接口方法 -->
<select id="getUserList" resultMap="UserMap">
select * from mybatis.userone;
</select>
6、日志
6.1、日志工厂
如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手
- 曾经:sout、debug
- 现在:日志工厂
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j (deprecated since 3.5.9)【掌握】
- JDK logging
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握】
- NO_LOGGING
在Mybatis中具体使用哪个日志实现,在设置中设定
-
STDOUT_LOGGING【标准日志输出】
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
Opening JDBC Connection Created connection 954702563. Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38e79ae3] ==> Preparing: select * from mybatis.userone; ==> Parameters: <== Columns: id, name, pwd <== Row: 1, zya, 123456 <== Row: 2, huake, 123 <== Row: 3, wuda, 123456 <== Row: 4, world, 1233 <== Row: 5, admin, 123 <== Total: 5 User(id=1, name=zya, password=123456) User(id=2, name=huake, password=123) User(id=3, name=wuda, password=123456) User(id=4, name=world, password=1233) User(id=5, name=admin, password=123) Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38e79ae3] Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38e79ae3] Returned connection 954702563 to pool. Process finished with exit code 0
6.2、LOG4J
log4j配置文件详解:https://blog.csdn.net/eagleuniversityeye/article/details/80582140
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
- 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
1、先导入LOG4J的包
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.A1.Encoding=UTF-8
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3、配置log4j为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4、log4j的使用,直接测试运行刚才的查询
简单使用:
- 1、在使用Log4j的类中,导入包
import org.apache.log4j.Logger;
- 2、日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(MyTest.class); // MyTest为相关的类
- 3、日志级别
logger.info("info:进入了testLog4j"); logger.debug("debug:进入了testLog4j"); logger.error("error:进入了testLog4j");
7、分页
思考:为什么要分页?
- 减少数据的处理量
使用Limit分页(从第0页开始查,每次查两个数据)
select * from userone limit 0, 2;
使用Mybatis实现分页,核心SQL
- 1、接口
// 分页 List<User> getUserByLimit(Map<String, Integer> map);
- 2、Mapper.xml
<select id="getUserByLimit" parameterType="map" resultType="User" resultMap="UserMap"> select * from userone limit #{startIndex}, #{pageSize}; </select>
- 3、测试
@Test public void test05(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 方式一: getMapper HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("startIndex", 1); map.put("pageSize", 2); List<User> limitList = sqlSession.getMapper(UserMapper.class).getUserByLimit(map); for (User user : limitList) { System.out.println(user); } }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } }
RowBounds:
关于RowBounds代码解析
public class RowBounds {
public static final int NO_ROW_OFFSET = 0;
public static final int NO_ROW_LIMIT = 2147483647;
public static final RowBounds DEFAULT = new RowBounds();
private final int offset; // 从第几页开始
private final int limit; // 显示多少条数据
// 无参构造
public RowBounds() {
this.offset = 0;
this.limit = 2147483647;
}
// 有参构造
public RowBounds(int offset, int limit) {
this.offset = offset;
this.limit = limit;
}
public int getOffset() {
return this.offset;
}
public int getLimit() {
return this.limit;
}
}
不在使用SQL实现分页
- 1、接口
// 分页2 List<User> getUserByRowBounds();
- 2、mapper.xml
<!-- 通过RowBound进行分页查询 --> <select id="getUserByRowBounds" resultMap="UserMap"> select * from userone; </select>
- 3、测试
@Test public void test06(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 通过RowBounds实现 RowBounds rowBounds = new RowBounds(1, 2); // 通过Java代码层面实现分页 List<User> userList = sqlSession.selectList("com.zya.dao.UserMapper.getUserByRowBounds", null, rowBounds); for (User user:userList) { System.out.println(user); } }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } }
MyBatis 分页插件 PageHelper,了解即可
8、使用注解开发
面向接口编程
- 之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
- 根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都会遵守共同的标准,使得开发变得容易,规范性更好
- 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了
- 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通道,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是按照这种思想来编程
关于接口的理解
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离
- 接口的本身反映了系统设计人员对系统的抽象理解
- 接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class)
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface)
- 一个体有可能有多个抽象面。抽象体与抽象面是有区别的
三个面向区别
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构。
使用注解开发:
1、注解在接口上实现
public interface UserMapper {
@Select("select * from userone")
List<User> getUsers();
}
2、需要在核心配置文件(mybatis-config.xml)中绑定接口
<!-- 绑定接口 -->
<mappers>
<mapper class="com.zya.dao.UserMapper" />
</mappers>
3、测试
@Test
public void test01(){
// 第一步: 获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
// 底层主要运用反射
List<User> userList = sqlSession.getMapper(UserMapper.class).getUsers();
for (User user:userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 关闭SqlSession
sqlSession.close();
}
}
本质:反射机制实现
底层:动态代理!
Mybatis详细的执行流程:(重点!!!)
CRUD
我们可以在工具类创建的时候实现自动创建事务
-
1、自动提交事务
public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); }
-
2、编写接口,增加注解
public interface UserMapper { @Select("select * from userone") List<User> getUsers(); @Select("select * from userone where id=#{id}") User getUserById(@Param("id") int id); @Insert("insert into userone(id, name, pwd) values (#{id}, #{name}, #{pwd})") int addUser(User user); @Update("update userone set name=#{name}, pwd=#{pwd} where id=#{id}") int updateUser(User user); @Delete("delete from userone where id=#{id}") int deleteUser(@Param("id") int id); }
-
3、测试类【注意:我们必须要讲接口注册绑定到我们的核心配置文件中】
public class MyTest { @Test public void test01(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 List<User> userList = sqlSession.getMapper(UserMapper.class).getUsers(); for (User user:userList) { System.out.println(user); } }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } @Test public void test02(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 User user = sqlSession.getMapper(UserMapper.class).getUserById(2); System.out.println(user); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } @Test public void test03(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 sqlSession.getMapper(UserMapper.class).addUser(new User(6, "qh", "321")); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } @Test public void test04(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 sqlSession.getMapper(UserMapper.class).updateUser(new User(6, "beida", "132")); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } @Test public void test05(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 sqlSession.getMapper(UserMapper.class).deleteUser(6); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } } }
关于@Param()注解:
- 基本 类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议大家都加上!
- 我们在SQL中引用的就是我们这里的@Param(“id”)中设定的属性名
#{}和${}的区别
- 1、#{}是预编译处理,${}是字符串替换。
- 2、Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
- 3、Mybatis 在处理 时 , 就 是 把 {}时,就是把 时,就是把{}替换成变量的值。
- 4、使用#{}可以有效的防止 SQL 注入,提高系统安全性。
9、Lombok
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
- Java library
- plugs
- build tools
- with one annotation your class
使用步骤:
- 1、在IDEA中安装Lombok插件
- 2、在项目中导入lombok的jar包
<!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> <scope>compile</scope> </dependency>
- 3、注解方法
@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @var experimental @var @UtilityClass @ExtensionMethod (Experimental, activate manually in plugin settings)
常用说明:
- @Data:无参构造,getter,setter,toString,hashCode,equals
- @AllArgsConstructor:有参构造
- @NoArgsConstructor:无参构造
10、多对一处理
多对一:
- 多个学生,对应一个老师
- 对于学生这边而言,多个学生关联一个老师【多对一】
- 对于老师而言,集合,一个老师有很多学生【一对多】
SQL:
create table stu(
id int not null primary key,
name varchar(255),
tid int
)charset=utf8
create table teacher(
id int not null primary key,
name varchar(255),
foreign key(id) references stu(tid)
)charset=utf8
测试环境搭建:
- 1、导入lombok
<!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> <scope>compile</scope> </dependency>
- 2、新建实体类 Teacher、Student
@Data public class Student { private int id; private String name; // 学生需要关联一个老师 private Teacher teacher; }
@Data public class Teacher { private int id; private String name; }
- 3、新建Mapper接口
public interface TeacherMapper { @Select("select * from teacher where id=#{tid}") Teacher getTeacher(@Param("tid") int id); }
- 4、建立Mapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zya.dao.TeacherMapper"> </mapper>
- 5、在核心配置文件中绑定注册我们的Mapper接口或者文件
<mappers> <mapper class="com.zya.dao.TeacherMapper" /> <mapper class="com.zya.dao.StudentMapper" /> </mappers>
- 6、测试查询是否能够成功!
@Test public void test01(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 Teacher teacher = sqlSession.getMapper(TeacherMapper.class).getTeacher(1); System.out.println(teacher); }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } }
按照查询嵌套处理:
-
1、接口
public interface StudentMapper { // 查询所有的学生信息, 以及对应老师的信息 List<Student> getStudent(); }
-
2、Mapper.xml文件
<mapper namespace="com.zya.dao.StudentMapper"> <!-- 思路: 1、查询所有的学生的信息 2、根据查询出来的学生的tid, 寻找对应的老师 --> <select id="getStudent" resultMap="StudentTeacher"> select * from stu; </select> <resultMap id="StudentTeacher" type="Student"> <result property="id" column="id" /> <result property="name" column="name" /> <!-- 复杂的属性, 我们需要单独处理 对象: association 集合: collection --> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher" /> </resultMap> <select id="getTeacher" resultType="com.zya.pojo.Teacher"> select * from teacher where id=#{id}; </select> </mapper>
-
3、测试
@Test public void test01(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 List<Student> studentList = sqlSession.getMapper(StudentMapper.class).getStudent(); for (Student student : studentList) { System.out.println(student); } }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } }
按照结果嵌套处理:
- 1、接口
public interface StudentMapper { // 查询所有的学生信息, 以及对应老师的信息 List<Student> getStudent2(); }
- 2、Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zya.dao.StudentMapper"> <!-- ======================按照结果嵌套处理====================== --> <select id="getStudent2" resultMap="StudentTeacher2"> select s.id s_id, s.name s_name, t.name t_name from stu s, teacher t where s.tid = t.id; </select> <resultMap id="StudentTeacher2" type="Student"> <result property="id" column="s_id" /> <result property="name" column="s_name" /> <association property="teacher" javaType="Teacher"> <result property="name" column="t_name" /> </association> </resultMap> </mapper>
- 3、测试
@Test public void test01(){ // 第一步: 获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try { // 底层主要运用反射 List<Student> studentList = sqlSession.getMapper(StudentMapper.class).getStudent2(); for (Student student : studentList) { System.out.println(student); } }catch (Exception e){ e.printStackTrace(); }finally { // 关闭SqlSession sqlSession.close(); } }
回顾Mysql 多对一查询方式
- 子查询
- 联表查询
11、一对多处理
比如:一个老师拥有多个学生
对于老师而言,就是一对多的关系
- 1、环境搭建,和刚才一样
@Data public class Student { private int id; private String name; private int tid; }
@Data public class Teacher { private int id; private String name; // 一个老师拥有多个学生 private List<Student> students; }
按照结果嵌套处理
<select id="getTeacher" resultType="Teacher">
select * from teacher;
</select>
<!-- ====================按照结果嵌套查询==================== -->
<select id="getTeacher2" resultMap="TeacherStudent">
select s.id s_id, s.name s_name, t.name t_name, t.id t_id
from stu s, teacher t
where s.tid = t.id and t.id = #{tid};
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="t_id" />
<result property="name" column="t_name" />
<!--
javaType: 指定的属性类型
集合中的泛型信息, 我们使用ofType获取
-->
<collection property="students" ofType="Student">
<result property="id" column="s_id" />
<result property="name" column="s_name" />
<result property="tid" column="tid" />
</collection>
</resultMap>
按照查询嵌套处理
<!-- ====================按照结果嵌套查询==================== -->
<select id="getTeacher3" resultMap="TeacherStudent2">
select * from teacher where id=#{tid};
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id" />
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from stu where tid=#{tid};
</select>
小结:
- 1、关联-association【多对一】
- 2、集合-collection【一对多】
- 3、javaType & ofType
- 1、javaType用来指定实体类中属性的类型
- 2、ofType用来指定映射到List或者集合中的 pojo类型,泛型中的约束类型
注意点:
- 保证SQL的可读性
- 注意一对多和多对一,属性名和字段的问题!
- 如果问题不好排查错误,可以使用日志,建议使用 Log4j
12、动态SQL
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句
利用动态 SQL,可以彻底摆脱这种痛苦
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
搭建环境:
CREATE TABLE 'blog' (
'id' varchar(50) NOT NULL COMMENT '博客id',
'title' varchar(100) NOT NULL COMMENT '博客标题',
'author' varchar(30) NOT NULL COMMENT '博客作者',
'create_time' datetime NOT NULL COMMENT '创建时间',
'views' int(30) NOT NULL COMMENT '浏览量'
)ENGINE=InnoDB DEFAULT CHARSET=utf8
创建一个基础工程
- 1、导包
- 2、编写配置文件
- 3、编写实体类
@Data public class Blog { private int id; private String title; private String author; private Date createTime; private int views; }
- 4、编写实体类对应的Mapper接口和Mapper.xml文件
if:使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。
<select id="queryBlog" parameterType="map" resultType="blog">
select * from blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="name">
and name = #{name}
</if>
</select>
choose、when、otherwise:有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
<select id="queryBlogChoose" resultType="blog" parameterType="map">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
trim、where、set:
<select id="queryBlog" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">title = #{title},</if>
<if test="author != null">author = #{author}</if>
</set>
<where>
id = #{id}
</where>
</update>
所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
SQL片段:有的时候,我们可能会将一些功能的部分抽取出来,方便复用
- 1、使用SQL标签抽取公共的部分
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
- 2、在需要使用的地方使用include标签引用即可
<select id="queryBlog" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-author"></include> </where> </select>
注意事项:
- 最好基于单表来定义SQL片段
Foreach:动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
<!--
select * from blog where 1=1 and (id=1 or id=2 or id=3)
我们现在传递一个万能的map, 这map中可以存在一个集合!
-->
<select id="queryBlogForeach" resultType="blog" parameterType="map">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了
建议:
- 现在MySql中写出完整的SQL,再对应的去修改成为我们的动态SQL实现通用即可!