一、Mybatis的使用:
Mybatis是一款优秀的持久层(负责将数据保存到数据库额那一层)框架,用于简化JDBC的开发。
框架的概念是:框架是一个半成品的软件,是一套可重用的、通用的、软件基础代码模型。
第1步:环境准备
1. 创建表,导数据:
复制下面代码到Navicat,创建数据库mybatis,新建表tb_user:
create database mybatis;
use mybatis;
drop table if exists tb_user;
create table tb_user(
id int primary key auto_increment,
username varchar(20),
password varchar(20),
gender char(1),
addr varchar(30)
);
INSERT INTO tb_user VALUES(1,'zhangsan','123','男','北京');
INSERT INTO tb_user VALUES(2,'李四','234','女','天津');
INSERT INTO tb_user VALUES(3,'王五','11','男','西安');
在Navicat中点击 查询-新建查询-粘贴代码-运行:
刷新一下就将表插入成功,数据成功显示:
2. 创建模块,导入坐标
Project Structure - Modules - New Module,新建项目:
创建名为mybatis-demo的子文件夹:
在pom.xml文件里,导入mybatis、mysql、junit单元测试、slf4j日志的依赖和驱动模块,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mybatis-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--mybatis 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--junit 单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- 添加slf4j日志api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- 添加logback-classic依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 添加logback-core依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</project>
第2步:编写MyBatis核心配置文件
指定数据库的连接信息,避免硬编码(将连接信息直接写死在代码或配置文件中)问题。
在resources下创建mybatis-config.xml配置文件,粘贴如下内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="111111"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="UserMapper.xml"/>
</mappers>
</configuration>
通过配置<mappers>元素,我们可以将Mapper接口与对应的映射文件关联起来,使MyBatis自动解析和执行Mapper接口中定义的SQL语句。
<package>元素指定SQL映射文件(UserMapper.xml),MyBatis会自动扫描该配置文件,注册到MyBatis的配置中。
当MyBatis解析到Mapper接口时,会根据接口的名称和映射文件的规则,自动查找对应的映射文件并加载其中的SQL语句。这样,我们可以通过调用Mapper接口的方法来执行对应的SQL语句,而无需编写繁琐的SQL语句和映射配置。
第3步:编写SQL映射文件
书写sql语句,统一进行管理,避免硬编码问题。
在resources下创建UserMapper.xml,粘贴如下代码:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
<select id="selectAll" resultType="com.itheima.pojo.User">
select * from tb_user;
</select>
</mapper>
id属性指定了SQL操作的唯一标识符,resultType指定了查询结果的类型,<select>标签内使具体的SQL查询语句。
第4步:启动
1. 创建pojo实体类
在java下创建com.itheima.pojo.User类。按住Alt+insert——可以设置Set方法和toString方法:
2. 创建启动类
在java下创建com.itheima.MybatisDemo,在其中写入如下代码:
//1. 加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml"; //mybatis-config.xml是核心配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.执行SQL语句
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);
//4.释放资源
sqlSession.close();
第1、2步实现与数据库的连接,第3步创建接口,第4步创建对象,第5步调用SQL语句查询返回结果。
1. String resource = "mybatis-config.xml" :定义了一个字符串变量resource,用于存储MyBatis的核心配置文件路径
2. InputStream inputStream = Resources.getResourceAsStream(resource) :通过Resources.getResourceAsStream()方法加载核心配置文件并返回一个InputStream对象。
3. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream) :使用SqlSessionFactoryBuilder类的build()方法创建SqlSessionFactory对象。SqlSessionFactory是MyBatis的核心接口,用于创建SqlSession对象,从而执行数据库操作。
4. SqlSession sqlSession = sqlSessionFactory.openSession()通过SqlSessionFactory的openSession()方法创建一个SqlSession对象。SqlSession是MyBatis的会话对象,可以用来执行SQL语句和管理事务。
5. List<User> users = sqlSession.selectList("test.selectAll") :调用SqlSession对象的selectList()方法执行SQL查询操作,将结果封装为一个List<User>类型的对象。其中test.selectAll是映射文件中查询语句的唯一标识符
可以正常输出数据库内容:
二、Mapper代理开发
在前面代码的基础上进行增添和修改。
第1步:添加接口
1. 定义与SQL映射文件同名的Mapper接口——在java-com-itheima下创建mapper包,定义UserMapper接口:
在接口中写入返回类型和SQL语句的唯一标识(SQL映射文件中对应SQL语句的id)同名的方法:
List<User> selectAll();
第2步:统一目录
2. 将Mapper接口和SQL映射文件放置在同一目录下——把UserMapper接口和配置文件放在同一层目录下
在resources下创建com-itheima-mapper包,将UserMapper.xml放在mapper包下:
第3步:修改SQL映射文件
3. 设置SQL映射文件的namespace属性为Mapper接口全限定名(完整路径)
第4步:修改MyBatis核心配置文件
4. 修改MyBatis核心配置文件中SQL映射文件地址
UserMapper.xml的路径有所改动,右键 - Copy Path/Referenc - Path From Source Root 获取更新路径:
<mappers>元素是MyBatis配置文件中的一个元素,用于指定Mapper接口的扫描配置。
<package>元素用于指定要扫描的Mapper接口所在的包名。
<mappers>
<package name="com.itheima.mapper"/>
</mappers>
第5步:修改执行类
5. 在MyBatisDemo中修改执行SQL的部分,通过SqlSession的getMapper方法获取Mapper接口的代理对象:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
6. 调用对应方法执行SQL:
List<User> users = userMapper.selectAll();
System.out.println(users);
UserMapper接口的定义可以将对数据库的操作与具体的实现(UserMapper.xml实现)分离开来,让开发人员能够更方便地理解和使用这些数据库操作。
SqlSessionFactory是MyBatis的核心类之一,用于创建和管理SqlSession对象。
SqlSession对象获取UserMapper接口时,是通过MyBatis的动态代理机制来生成UserMapper接口的代理对象。这个代理对象包含了具体的数据库操作逻辑,实现了接口中定义的方法。
然后,代理对象会根据接口的方法名和参数信息,【找到对应的Mapper XML文件中的SQL语句】,这是通过命名规则和Mapper XML文件的配置来实现的。最后,调用的方法会执行相应的SQL语句,并返回结果。
三、核心配置文件完成查询
environments:可以配置多个environment通过default属性切换不同的environment,配置数据库连接环境信息。
resulttype:类型别名
3.1 前期准备
新建数据库:
drop table if exists tb_brand;
create table tb_brand
(
id int primary key auto_increment,
brand_name varchar(20),
company_name varchar(20),
ordered int,
description varchar(100),
status int
);
insert into tb_brand(brand_name,company_name,ordered,description,status)
values('三只松鼠','三只松鼠股份有限公司',5,'好吃不上火',0),
('华为','华为科技有限公司',100,'华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界',1),
('小米','小米科技有限公司',50,'are you ok',1);
SELECT * FROM tb_brand;
新建Brand实体类放在pojo目录下,在test目录下创建一个java包,新建MyBatisTest测试类:
3.2 查询所有数据
第1步:编写接口方法:
在mapper下创建BrandMapper接口,写入如下代码:
public interface BrandMapper {
public List<Brand> selectAll();
}
第2步:编写SQL映射文件:
定义名称空间为BrandMapper全类名,id写接口中的方法,resjltType写Brand实体类,在<select>中写SQL语句:
<mapper namespace="com.itheima.mapper.BrandMapper">
<select id="selectAll" resultType="com.itheima.pojo.Brand">
select * from tb_brand;
</select>
</mapper>
第3步:执行测试方法
在测试类中注意:1. 非静态方法(无static)2. 方法名无传入参数 3. 要加注解@Test才能编译运行
public class MyBatisTest {
@Test
public void testSelectAll() throws IOException {
//1.获取SqlSessionFactory
String resource = "mybatis-config.xml"; //mybatis-config.xml是核心配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
//5.释放资源
sqlSession.close();
}
}
下面是结果:
之所以出现很多null是因为数据库采用下划线命名,实体类采用驼峰命名:
可以有如下解决方案:1.起别名;2.sql片段。见文章末尾。3.resultMap,在这里重点讲解:
1. 定义<resultMap>标签,id里填写唯一标识符,提供给resultMap引用,type指示返回的实体类全类名。
2. 在<resultMap>下定义<result>,column中写数据库中的字段,property中写实体类中的字段。
3.3 查询特定数据
1. 编写接口方法:
在BrandMapper接口中写入方法(返回Brand实体类,传入参数名ID):
public Brand selectSingle(int ID);
2. 编写SQL映射文件
注意:id = #{ID},等号左边的id是实体类中的变量名,等号右边的ID是接口方法中传入的参数。
<select id="selectSingle" resultMap="brandResultMap">
select * from tb_brand where id=#{ID};
</select>
3. 执行方法,测试
@Test
public void testSelectById() throws IOException {
//接收参数
int id = 2;
//1.获取SqlSessionFactory
String resource = "mybatis-config.xml"; //mybatis-config.xml是核心配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3.获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4.执行方法
Brand brand = brandMapper.selectSingle(id);
System.out.println(brand);
//5.释放资源
sqlSession.close();
}
实现逻辑:首先在Test里获取一个参数id,然后调用brandMapper中的selectById方法,传入的参数是ID,然后相当于是调用了映射文件中的sql语句,返回了符合结果的Brand对象给brand,然后输出这个brand结果。
3.4 多条件查询
需求背景:输入多个查询参数进行模糊查询。
下面分3种传参方式(散装参数,对象参数,集合参数)分别进行说明:
3.4.1. 散装参数
1. 添加BrandMapper接口方法:
public List<Brand> selectByCondition(@Param("STATUS") int status, @Param("BRANDNAME") String brandName, @Param("COMPANYNAME") String companyName);
— — — — — — — —知识加油站 — — — — — — — — —
1. @Param注解是MyBatis框架中的一个注解,用于指定方法参数在SQL语句中的命名参数,它的作用是为了在SQL语句中引用方法参数时,提供一个具有可读性的名称。
User getUserByIdAndUsername(@Param("userId") int id, @Param("username") String name);
在上面的示例中@Param注解分别给id和name参数指定了名称,然后在SQL语句中使用#{userId}和#{username}来引用这两个参数。
2. %是一个通配符,用于模糊匹配字符串。它可以匹配任意数量的字符(包括零个字符)。
3. LIKE是一个用于模糊匹配的操作符。用于在文本字段中进行模糊匹配。
— — — — — — — — — — — — — — — — — —— — — —
2. 编写SQL映射文件:
#花括号里的内容是接口方法中@Param注解里定义的名称,等号左边是数据库中的字段。
注意:brand_name和company_name要进行模糊匹配
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
where status=#{STATUS} and company_name like #{COMPANYNAME} and brand_name like #{BRANDNAME};
</select>
3. 执行测试方法:
注意:修改了接收参数和执行SQL语句部分。
@Test
public void testSelectAll() throws IOException {
//— — — — — — — —修改1:接收参数— — — — — — — —
int status = 1;
String companyName = "华为";
String brandName = "华为";
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//— — — — — — — — — — — — — — — — — — —
//1. 加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml"; //mybatis-config.xml是核心配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2.获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//— — — — — — — —修改2:执行SQL语句— — — — — — — —
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> res= brandMapper.selectByCondition(status,brandName,companyName);
System.out.println(res);
//— — — — — — — — — — — — — — — — — — — — — — —
//4.释放资源
sqlSession.close();
}
3.4.2. 对象参数
1. 添加BrandMapper接口方法:
public List<Brand> selectByCondition(Brand brand);
2. 编写SQL映射文件:
等号左边写数据库表中的字段,等号右边写实体类中的字段:
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
where status=#{status} and company_name like #{companyName} and brand_name like #{brandName};
</select>
3. 执行测试方法:
int status = 1;
String companyName = "华为";
String brandName = "华为";
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//— — — — — — — —修改1:封装参数— — — — —
Brand brand = new Brand();
brand.setBrandName(brandName);
brand.setCompanyName(companyName);
brand.setStatus(status);
//— — — — — — — — — — — — — — — — — — —
//— — — — — — — —修改2:传入对象参数— — — — — — — —
List<Brand> res= brandMapper.selectByCondition(brand);
System.out.println(res);
//— — — — — — — — — — — — — — — — — — — — — — —
3.4.3. 集合参数
1. 添加BrandMapper接口方法:
public List<Brand> selectByCondition(Map map);
2. 编写SQL映射文件:同上
3. 执行测试方法:
int status = 1;
String companyName = "华为";
String brandName = "华为";
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//— — — — — — — —修改1:封装集合— — — — — — — —
Map map = new HashMap(); //注意new的是HashMap()
map.put("status",status);
map.put("companyName",companyName);
map.put("brandName",brandName);
//— — — — — — — — — — — — — — — — — — —
//— — — — — — — —修改2:传入对象参数— — — — — — — —
List<Brand> res= brandMapper.selectByCondition(map);
System.out.println(res);
//— — — — — — — — — — — — — — — — — — — — — — —
3.5 添加操作
1. 添加BrandMapper接口方法:
public void ADD(Brand brand);
2. 编写SQL映射文件:
<insert id="ADD">
insert into tb_brand(brand_name,company_name,ordered,description,status)
values(#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>
3. 执行测试方法:
//— — — — — — — —修改1:接收参数— — — — — — — —
String companyName = "波导手机";
String brandName = "波导";
String description="手机中的战斗机";
int ordered=100;
int status = 1;
//— — — — — — — — — — — — — — — — — — — — — —
//— — — — — — — —修改2:封装对象— — — — — — — —
Brand brand = new Brand();
brand.setBrandName(brandName);
brand.setStatus(status);
brand.setDescription(description);
brand.setCompanyName(companyName);
brand.setOrdered(ordered);
//— — — — — — — — — — — — — — — — — — — — —
//— — — — — — — —修改3:执行方法提交— — — — — — — —
brandMapper.ADD(brand);
sqlSession.commit();
//— — — — — — — — — — — — — — — — — — — — — — —
设置下面参数可以自动提交:
主键返回
useGeneratedKeys属性是一个布尔值,用于指定是否使用数据库自动生成的键。如果设置为true,MyBatis会将自动生成的键返回给Java对象。
keyProperty属性是一个字符串,用于指定返回的自动生成的键要映射到Java对象的哪个属性上。这个属性应该与Java对象的属性名相匹配(换言之对应的名要与Brand实体类中的变量对应)。
//— — — — — — — —打印Id值— — — — — — — —
sqlSession.commit();
System.out.println(brand.getId());
//— — — — — — — — — — — — — — — — — — —
3.6 修改操作
1. 修改全部字段
1. 添加BrandMapper接口方法:
public void update(Brand brand);
2. 编写SQL映射文件:
等号左边是数据库字段名,等号右边是Java对象的字段名(也就是实体类的字段名)。
<update id="update">
update tb_brand
set
brand_name=#{brandName},
company_name=#{companyName},
ordered=#{ordered},
description=#{description},
status=#{status}
where id=#{id};
</update>
3. 执行测试方法:
//— — — — — — 修改1:接收修改参数— — — — — —
String companyName = "66手机";
String brandName = "波导";
String description="手机中的7788";
int ordered=100;
int status = 1;
int id=4;
//— — — — — — — — — — — — — — — — — — — —
//— — — — — — — —修改2:封装对象— — — — — — — —
Brand brand = new Brand();
brand.setBrandName(brandName);
brand.setStatus(status);
brand.setDescription(description);
brand.setCompanyName(companyName);
brand.setOrdered(ordered);
brand.setId(id);
//— — — — — — — — — — — — — — — — — — — — —
//— — — — — — — —执行方法添加— — — — — — —
brandMapper.update(brand);
sqlSession.commit();
//— — — — — — — — — — — — — — — — — — —
2. 修改动态字段
1. 添加BrandMapper接口方法:
public void update(Brand brand);
2. 编写SQL映射文件:
通过使用<set>标签,可以在更新操作中根据条件动态地生成SET子句,只包含满足条件的列和对应的值。可以避免在生成的SQL语句中出现不必要的逗号或不完整的SET子句。
<update id="update">
update tb_brand
<set>
<if test="brandName != null and brandName != ''">
brand_name=#{brandName},
</if>
<if test="companyName != null and companyName != ''">
company_name=#{companyName},
</if>
<if test="ordered != null">
ordered=#{ordered},
</if>
<if test="description != null and description != ''">
description=#{description},
</if>
<if test="status != null ">
status=#{status}
</if>
</set>
where id=#{id};
</update>
3. 执行测试方法:
//— — — — — — 修改1:接收修改参数— — — — — —
String companyName = "Space-X!!";
int id=6;
//— — — — — — 修改2:封装对象— — — — — — — —
Brand brand = new Brand();
brand.setCompanyName(companyName);
brand.setId(id);
//— — — — — — — — — — — — — — — — — — — — —
//— — — — — — — —执行方法添加— — — — — — —
brandMapper.update(brand);
sqlSession.commit();
3.7 删除操作
1. 删除单个
1. 添加BrandMapper接口方法:
public void delete(int id);
2. 编写SQL映射文件:
<delete id="deleteById">
delete from tb_brand where id = #{id};
</delete>
3. 执行测试方法:
//— — — — — — — —执行删除方法— — — — — — —
int num=8;
brandMapper.deleteById(num);
sqlSession.commit();
//— — — — — — — — — — — — — — — — — — —
2. 删除多个
1. 添加BrandMapper接口方法:
要添加@Param注解,因为是多个元素。
ids:。
public void deleteById(@Param("ids") int[] ids);
2. 编写SQL映射文件:
collection:。item:。sepatator:。
<delete id="deleteById">
delete from tb_brand where id
in(
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
);
</delete>
3. 执行测试方法:
//— — — — — — — —执行删除方法— — — — — — —
int [] ids={5,6,7};
brandMapper.deleteById(ids);
sqlSession.commit();
//— — — — — — — — — — — — — — — — — — —
四、注解完成增删改查
注解用来完成简单功能(不适合动态SQL要if和foreach这些),配置文件完成复杂功能
5.1 查询@Select
1. 查询所有:
更改:编写SQL映射文件 -> 编写注解
@Select("select * from tb_brand")
@Result(column = "brand_name", property = "brandName")
@Result(column = "company_name", property = "companyName")
public List<Brand> selectAll();
2. 查询单个:
更改:编写SQL映射文件 -> 编写注解
@Select("select * from tb_brand where id=#{ID}")
@Result(column = "brand_name", property = "brandName")
@Result(column = "company_name", property = "companyName")
public Brand selectSingle(int ID);
5.2 添加@Insert
更改:编写SQL映射文件 -> 编写注解
@Insert("insert into tb_brand(brand_name,company_name,ordered,description,status)" +
"values(#{brandName},#{companyName},#{ordered},#{description},#{status})")
public void ADD(Brand brand);
5.3 修改@Update
注解只适合修改某条数据的全部字段,否则会出现为null的情况:
@Update("update tb_brand set brand_name=#{brandName},company_name=#{companyName},ordered=#{ordered},description=#{description},status=#{status} where id =#{id}")
public void update(Brand brand);
5.4 删除@Delete
注解只适合删除传入单个id的,多id的使用配置文件:
@Delete("delete from tb_brand where id=#{id}")
public void deleteById(int id);
五、动态条件查询
应用场景:用户不一定填写完所有的查找条件,程序根据部分给定的查找条件完成查找。写在Mapper.xml配置文件里。
1. if 语法如下:
<if test="条件">
判断
</if>
如果没有输入条件,应该输出所有,但下例会没有输出,不符合要求,因此可以加入一个恒等式:
第2种方法可以用<where>标签替换where,这也是以后的默认做法:
2.choose 语法如下:
<choose>
<when test="条件">
判断
</when>
<otherwise>
判断
<otherwise>
</choose>
choose相当于是switch,when相当于是case,otherwise相当于是default。
下面的写法在末尾加上otherwise,可以满足无输入的情况:
用<where>标签包裹是更好的方法,可以不写otherwise:
六、心得总结
6.1 学习复盘
1. 按住Alt+鼠标左键——可以对多行同时进行编辑。
6.2 面试真题
1、mybatis 中 #{}和 ${}的区别是什么?
- #{}带引号,${}不带引号;
- #{}可以防止SQL注入;
- ${}常用于数据库表名、order by子句;
- 一般能用#{}就不要使用${};
2、mybatis 是否支持延迟加载?延迟加载的原理是什么?
延迟加载其实就是讲数据加载时机推迟,比如推迟嵌套查询的时机。
延迟加载可以实现先查询主表,按需实时做关联查询,返回关联表结果集,一定程度上提高了效率。
mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,collection是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false。
3、延迟加载的原理是什么?
使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。
比如调用a.getB().getName(),拦截器方法invoke()发现a.getB()为null,会单独发送事先准备好的查询关联B对象的sql语句,把B查询出来然后调用a.setB(b),也是a的对象的属性b就有值了,然后调用getName(),这就是延迟加载的原理。
4、mybatis一级缓存、二级缓存
1、一级缓存:指的是mybatis中sqlSession对象的缓存,当我们执行查询以后,查询的结果会同时存入sqlSession中,再次查询的时候,先去sqlSession中查询,有的话直接拿出,当sqlSession消失时,mybatis的一级缓存也就消失了,当调用sqlSession的修改、添加、删除、commit()、close()等方法时,会清空一级缓存。
2、二级缓存:指的是mybatis中的sqlSessionFactory对象的缓存,由同一个sqlSessionFactory对象创建的sqlSession共享其缓存,但是其中缓存的是数据而不是对象。当命中二级缓存时,通过存储的数据构造成对象返回。查询数据的时候,查询的流程是二级缓存 > 一级缓存 > 数据库。
3、如果开启了二级缓存,sqlSession进行close()后,才会把sqlSession一级缓存中的数据添加到二级缓存中,为了将缓存数据取出执行反序列化,还需要将要缓存的pojo实现Serializable接口,因为二级缓存数据存储介质多种多样,不一定只存在内存中,也可能存在硬盘中。
4、mybatis框架主要是围绕sqlSessionFactory进行的,具体的步骤:
定义一个configuration对象,其中包含数据源、事务、mapper文件资源以及影响数据库行为属性设置settings。
通过配置对象,则可以创建一个sqlSessionFactoryBuilder对象。
通过sqlSessionFactoryBuilder获得sqlSessionFactory实例。
通过sqlSessionFactory实例创建qlSession实例,通过sqlSession对数据库进行操作。
5、代码实例
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 resource="db.properties"/>
<!-- 设置类型别名 -->
<typeAliases>
<typeAlias type="cn.itcast.javaee.mybatis.app04.Student" alias="student"/>
</typeAliases>
<!-- 设置一个默认的连接环境信息 -->
<environments default="mysql_developer">
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="mysql_developer">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="oracle_developer">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver" value="${oracle.driver}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件-->
<mappers>
<mapper resource="cn/itcast/javaee/mybatis/app14/StudentMapper.xml"/>
</mappers>
</configuration>
public class MyBatisTest {
public static void main(String[] args) {
try {
//读取mybatis-config.xml文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//初始化mybatis,创建SqlSessionFactory类的实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//创建session实例
SqlSession session = sqlSessionFactory.openSession();
/*
* 接下来在这里做很多事情,到目前为止,目的已经达到得到了SqlSession对象.通过调用SqlSession里面的方法,
* 可以测试MyBatis和Dao层接口方法之间的正确性,当然也可以做别的很多事情,在这里就不列举了
*/
//插入数据
User user = new User();
user.setC_password("123");
user.setC_username("123");
user.setC_salt("123");
//第一个参数为方法的完全限定名:位置信息+映射文件当中的id
session.insert("com.cn.dao.UserMapping.insertUserInformation", user);
//提交事务
session.commit();
//关闭session
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、mybatis如何防止sql注入
注意:但凡是sql注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成sql语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的【外部数据不可信任】的原则,纵观web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到,就是变量的检测、过滤、验证下手,确保变量是开发者所预想的。
1、检查变量数据类型和格式
数据类型检查,sql执行前,要进行数据类型检查,如果是邮箱,参数就必须是邮箱的格式,如果是日期,就必须是日期格式;
只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
如果上述例子中id是int型的,效果会怎样呢?无法注入,因为输入注入参数会失败。比如上述中的name字段,我们应该在用户注册的时候,就确定一个用户名规则,比如5-20个字符,只能由大小写字母、数字以及汉字组成,不包含特殊字符。此时我们应该有一个函数来完成统一的用户名检查。不过,仍然有很多场景并不能用到这个方法,比如写博客,评论系统,弹幕系统,必须允许用户可以提交任意形式的字符才行,否则用户体验感太差了。
2、过滤特殊符号
3、绑定变量,使用预编译语句
6、MyBatis中 #{}和${}的区别是什么
#{}是预编译处理,${}是字符串替换;
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
7、Mybatis 中一级缓存与二级缓存
MyBatis的缓存分为一级缓存和 二级缓存。
一级缓存是SqlSession级别的缓存,默认开启。
二级缓存是NameSpace级别(Mapper)的缓存,多个SqlSession可以共享,使用时需要进行配置开启。
缓存的查找顺序:二级缓存 => 一级缓存 => 数据库
8、MyBatis如何获取自动生成的(主)键值
在<insert>标签中使用 useGeneratedKeys和keyProperty 两个属性来获取自动生成的主键值。
示例:
<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>
insert into names (name) values (#{name})
</insert>
Java
9、简述Mybatis的动态SQL,列出常用的6个标签及作用
动态SQL是MyBatis的强大特性之一 基于功能强大的OGNL表达式。
动态SQL主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询
常用的标签:
<if> : 进行条件的判断
<where>:在<if>判断后的SQL语句前面添加WHERE关键字,并处理SQL语句开始位置的AND 或者OR的问题
<trim>:可以在SQL语句前后进行添加指定字符 或者去掉指定字符.
<set>: 主要用于修改操作时出现的逗号问题
<choose> <when> <otherwise>:类似于java中的switch语句.在所有的条件中选择其一
<foreach>:迭代操作
10、Mybatis 如何完成MySQL的批量操作
MyBatis完成MySQL的批量操作主要是通过<foreach>标签来拼装相应的SQL语句
例如:
<insert** id="insertBatch" >
insert into tbl_employee(last_name,email,gender,d_id) values
<foreach** collection="emps" item="curr_emp" separator=","**>
(#{curr_emp.lastName},#{curr_emp.email},#{curr_emp.gender},#{curr_emp.dept.id})
</foreach>
</insert>
6.3 常见问题
6.3.1 IDEA不识别数据库解决方案
在IDEA的Database中选择Data Source-MySQL:
填写用户名、密码和数据库名称:
如果目标数据库下没有tb_user表,就点击那个小扳手:
然后到Schemas下勾选需要显示表的数据库即可:
以后可以直接在这里写sql语句并执行:
6.3.2 resource文件缺失
点击File-Project Structure,选中要作为resources文件的目录,右键点击Resources
6.3.3 MybatisX插件下载
下载一个MyBatisX插件:
6.3.4 数据库和实体类中的变量名不一致
方法1:起别名(BrandMapper.xml中的sql语句,对不一样的列名起别名,让别名和实体类的属性名一样)
方法2:sql片段
<mapper namespace="com.itheima.mapper.BrandMapper">
<sql id="brand_column">
id,brand_name as brandName,company_name as companyName,ordered,description,status
</sql>
<select id="selectAll" resultType="com.itheima.pojo.Brand">
select
<include refid="brand_column"></include>
from tb_brand;
</select>
</mapper>
方法3:resultMap
1. 定义<resultMap>标签,id里填写唯一标识符,提供给resultMap引用,type指示返回的实体类全类名。
2. 在<resultMap>下定义<result>,column中写数据库中的字段,property中写实体类中的字段。
七、Mybatis重要知识点:
1.1 参数占位符
一共分为2种:#{}和${}。
其中#{}会被替换为问号(?),可以防止sql注入。 ${}会变成具体值,会存在sql注入的问题。
使用时机:参数传递的情况用#{}。表名或者列名不固定的情况下,用${}。
2.2 特殊字符处理
转义字符:小于 <
CDATA区:<![CDATA[ 在此填写内容 ]]>