给出指定数据表,完成查询所有
1.1 环境准备
- 数据库表(tb_brand)及数据准备
sql -- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand (
-- id 主键
id int primary key auto_increment,
-- 品牌名称
brand_name varchar(20),
-- 企业名称
company_name varchar(20),
-- 排序字段 ordered int,
-- 描述信息 description varchar(100),
-- 状态:0:禁用 1:启用 status int );
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status) values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0), ('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1), ('小米', '小米科技有限公司', 50, 'are you ok', 1);
- 实体类 Brand
在 com.itheima.pojo
包下创建 Brand 实体类。
package com.pl.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Brand {
private Integer id;
private String brandName;
private String companyName;
private Integer ordered;
private String description;
private Integer status;
}
dao层接口以及映射文件:
BrandMapper接口
package com.pl.dao;
import com.pl.pojo.Brand;
import java.util.List;
public interface BrandMapper {
List<Brand> selectAll();
}
BrandMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD MApper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pl.dao.BrandMapper">
<!-- 这种方法查出出来,有些封装失败,查不出来,因为Brand中与数据库中名字不对应-->
<select id="selectAll" resultType="brand">
select *
from tb_brand;
</select>
</mapper>
Brand实体类:
package com.pl.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Brand {
private Integer id;
private String brandName;
private String companyName;
private Integer ordered;
private String description;
private Integer status;
}
工具类:
package com.pl.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;//提升作用域
static {
try {
//获取SQLSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取SqlSession连接
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}//commit不需要再写,自动提交事务
}
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="jdbc.properties"/>-->
<typeAliases>
<package name="com.pl.pojo"/>
</typeAliases>
<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=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件-->
<mappers>
<mapper resource="com/pl/dao/BrandMapper.xml"/>
</mappers>
</configura
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>com.pl</groupId>
<artifactId>ExerciseMybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.github.dongfg.plugin</groupId>-->
<!-- <artifactId>mybatis-generator-lombok-plugin</artifactId>-->
<!-- <version>1.0.0</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.junit.jupiter</groupId>-->
<!-- <artifactId>junit-jupiter</artifactId>-->
<!-- <version>RELEASE</version>-->
<!-- <scope>compile</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
测试结果:
部分查询结果为空
解决办法1:我们可以在写sql语句时给这两个字段起别名,将别名定义成和属性名一致即可。
解决办法2:使用resultMap解决
起别名 + sql片段的方式可以解决上述问题,但是它也存在问题。如果还有功能只需要查询部分字段,而不是查询所有字段,那么我们就需要再定义一个 SQL 片段,这就显得不是那么灵活。
通过更改代码可以实现查询到所有数据
<select id="selectAll" resultMap="brandResultMap">
select * from tb_brand;
</select>
<resultMap id="brandResultMap" type="brand">
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
小结
实体类属性名 和 数据库表列名 不一致,不能自动封装数据
- ==起别名:==在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样
- 可以定义 片段,提升复用性
- ==resultMap:==定义 完成不一致的属性名和列名的映射
练习2:根据id查询用户详情
接口类定义方法:
Brand selectById(int id);
Mapper文件中配置方法:
<select id="selectById" resultType="brand" parameterType="int">
select * from tb_brand where id = #{id};
</select>
测试方法:
@Test
public void testSelectById() throws IOException {
int id = 1;
SqlSession session = MybatisUtils.getSession();
BrandMapper mapper = session.getMapper(BrandMapper.class);
Brand brand = mapper.selectById(id);
System.out.println(brand);
session.close();
}
结果:
参数占位符详解:
mybatis提供了两种参数占位符:
- #{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。从上述例子可以看出使用#{} 底层使用的是
PreparedStatement
- ${} :拼接SQL。底层使用的是
Statement
,会存在SQL注入问题。如下图将 映射配置文件中的 #{} 替换成 ${} 来看效果
练习: SQL语句中特殊字段处理
parameterType使用
可以看出报错了,因为映射配置文件是xml类型的问题,而 > < 等这些字符在xml中有特殊含义,所以此时我们需要将这些符号进行转义,可以使用以下两种方式进行转义
- 转义字符
下图的 <
就是 <
的转义字符。
多条件查询:(模糊查询)
编写接口方法
在 BrandMapper
接口中定义多条件查询的方法。
而该功能有三个参数,我们就需要考虑定义接口时,参数应该如何定义。Mybatis针对多参数有多种实现
- 使用
@Param("参数名称")
标记每一个参数,在映射配置文件中就需要使用#{参数名称}
进行占位
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);
- 将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用
#{内容}
时,里面的内容必须和实体类属性名保持一致。
List<Brand> selectByCondition(Brand brand);
- 将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用
#{内容}
时,里面的内容必须和map集合中键的名称一致。
List<Brand> selectByCondition(Map map);
编写SQL语句
在 BrandMapper.xml
映射配置文件中编写 statement
,使用 resultMap
而不是使用 resultType
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
编写测试方法
@Test
public void testSelectByCondition() {
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
// 处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
SqlSession session = MybatisUtils.getSession();
BrandMapper mapper = session.getMapper(BrandMapper.class);
//方式一 :接口方法参数使用 @Param 方式调用的方法
List<Brand> brands = mapper.selectByCondition(status, companyName, brandName);
//方式二 :接口方法参数是 实体类对象 方式调用的方法
//封装对象
/* Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);*/
//List<Brand> brands = brandMapper.selectByCondition(brand);
//方式三 :接口方法参数是 map集合对象 方式调用的方法
Map map = new HashMap();
map.put("status" , status);
map.put("companyName", companyName);
map.put("brandName" , brandName);
List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
session.close();
动态SQL
上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的SQL语句就不能那样写的
例如用户只输入 当前状态 时,SQL语句就是
select * from tb_brand where status = #{status}
而用户如果只输入企业名称时,SQL语句就是
select * from tb_brand where company_name like #{companName}
而用户如果输入了 当前状态
和 企业名称
时,SQL语句又不一样
select * from tb_brand where status = #{status} and company_name like #{companName}
针对上述的需要,Mybatis对动态SQL有很强大的支撑:
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
- if 标签:条件判断
- test 属性:逻辑表达式
改写selectbycondition:
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand where
<if test="status != null"> and status = #{status}</if>
<if test="companyName != null and companyName != '' "> and company_name like #{companyName} </if>
<if test="brandName != null and brandName != '' "> and brand_name like #{brandName} </if>
</select>
如上的这种SQL语句就会根据传递的参数值进行动态的拼接。如果此时status和companyName有值那么就会值拼接这两个条件。
但是它也存在问题,如果此时给的参数值是
java Map map = new HashMap(); // map.put("status" , status); map.put("companyName", companyName); map.put("brandName" , brandName);
拼接的SQL语句就变成了
sql select * from tb_brand where and company_name like ? and brand_name like ?
而上面的语句中 where 关键后直接跟 and 关键字,这就是一条错误的SQL语句。这个就可以使用 where 标签解决
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
<where>
<if test="status != null"> and status = #{status}</if>
<if test="companyName != null and companyName != '' "> and company_name like #{companyName} </if>
<if test="brandName != null and brandName != '' "> and brand_name like #{brandName} </if>
</where>
</select>
单个条件(动态SQL):
如上图所示,在查询时只能选择 品牌名称
、当前状态
、企业名称
这三个条件中的一个,但是用户到底选择哪儿一个,我们并不能确定。这种就属于单个条件的动态SQL语句。
这种需求需要使用到 choose(when,otherwise)标签
实现, 而 choose
标签类似于Java 中的switch语句。
通过一个案例来使用这些标签
编写接口方法
在 BrandMapper
接口中定义单条件查询的方法。
List<Brand> selectByConditionSingle(Brand brand);
配置方法:
在 BrandMapper.xml
映射配置文件中编写 statement
,使用 resultMap
而不是使用 resultType
<select id="selectByConditionSingle" resultMap="brandResultMap">
select * from tb_brand
<where>
<choose>
<when test="status != null and companyName != '' "></when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like #{brandName}
</when>
</choose>
</where>
</select>
测试方法:
@Test
public void testSelectByConditionSingle(){
int status = 1;
String companyName = "华为";
String brandName = "小米";
companyName = "%" +companyName + "%";
brandName = "%" +brandName +"%";
//封装对象
Brand brand = new Brand();
// brand.setCompanyName(companyName);
// brand.setStatus(status);
brand.setBrandName(brandName);
SqlSession session = MybatisUtils.getSession();
BrandMapper mapper = session.getMapper(BrandMapper.class);
List<Brand> brands = mapper.selectByConditionSingle(brand);
System.out.println(brands);
session.close();
}
添加数据
如上图是我们平时在添加数据时展示的页面,而我们在该页面输入想要的数据后添加 提交
按钮,就会将这些数据添加到数据库中。接下来我们就来实现添加数据的操作。
- 编写接口方法
-
void add(Brand brand);
编写SQL语句:
-
<select id="add"> insert into tb_brand(brand_name,company_name,ordered,description,status) values (#{brandName},#{companyName},#{ordered},#{description},#{status}); </select>
测试方法:
-
@Test public void testAdd() { //接收参数 int status = 1; String companyName = "波导手机"; String brandName = "波导"; String description = "手机中的战斗机"; int ordered = 100; //封装对象 Brand brand = new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); SqlSession session = MybatisUtils.getSession(); BrandMapper mapper = session.getMapper(BrandMapper.class); mapper.add(brand); session.close(); }
添加-主键返回
-
在数据添加成功后,有时候需要获取插入数据库数据的主键(主键是自增长)。
比如:添加订单和订单项,如下图就是京东上的订单
添加订单数据
在 insert 标签上添加如下属性:
- useGeneratedKeys:是够获取自动增长的主键值。true表示获取
- keyProperty :指定将获取到的主键值封装到哪儿个属性里
批量删除:
接口:
void deleteByIds(int[] ids);
实现方法:
在 BrandMapper.xml
映射配置文件中编写删除多条数据的 statement
。
编写SQL时需要遍历数组来拼接SQL语句。Mybatis 提供了 foreach
标签供我们使用
foreach 标签
用来迭代任何可迭代的对象(如数组,集合)。
- collection 属性:
- mybatis会将数组参数,封装为一个Map集合。
- 默认:array = 数组
- 使用@Param注解改变map集合的默认key的名称
- item 属性:本次迭代获取到的元素。
- separator 属性:集合项迭代之间的分隔符。
foreach
标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。 - open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
- close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
<delete id="deleteByIds">
delete from tb_brand where id
in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
;
</delete>
测试方法:
public void testDeleteByIds() {
int[] ids = {1,2,3};
SqlSession sqlSession = MybatisUtils.getSession();
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.deleteByIds(ids);
//5. 释放资源
sqlSession.close();
}
多个参数:
如下面的代码,就是接收两个参数,而接收多个参数需要使用 @Param
注解,那么为什么要加该注解呢?这个问题要弄明白就必须来研究Mybatis 底层对于这些参数是如何处理的。
User select(@Param("username") String username,@Param("password") String password);
<select id="select" resultType="user">
select *
from tb_user
where
username=#{username}
and password=#{password}
</select>
我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param
注解时有以下命名规则:
- 以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0",参数值1);
map.put("arg1",参数值2);
- 以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1",参数值1);
map.put("param2",参数值2);
代码验证:
- 在
UserMapper
接口中定义如下方法
java User select(String username,String password);
- 在
UserMapper.xml
映射配置文件中定义SQL
xml <select id="select" resultType="user"> select * from tb_user where username=#{arg0} and password=#{arg1} </select>
或者
xml <select id="select" resultType="user"> select * from tb_user where username=#{param1} and password=#{param2} </select>
在映射配合文件的SQL语句中使用用 arg
开头的和 param
书写,代码的可读性会变的特别差,此时可以使用 @Param
注解。
在接口方法参数上使用 @Param
注解,Mybatis 会将 arg
开头的键名替换为对应注解的属性值。
代码验证:
- 在
UserMapper
接口中定义如下方法,在username
参数前加上@Param
注解
java User select(@Param("username") String username, String password);
Mybatis 在封装 Map 集合时,键名就会变成如下:
map.put("username",参数值1);
map.put("arg1",参数值2);
map.put("param1",参数值1);
map.put("param2",参数值2);
- 在
UserMapper.xml
映射配置文件中定义SQL
xml <select id="select" resultType="user"> select * from tb_user where username=#{username} and password=#{param2} </select>
- 运行程序结果没有报错。而如果将
#{}
中的username
还是写成arg0
xml <select id="select" resultType="user"> select * from tb_user where username=#{arg0} and password=#{param2} </select>
代码验证:
- 在
UserMapper
接口中定义如下方法,在username
参数前加上@Param
注解
java User select(@Param("username") String username, String password);
Mybatis 在封装 Map 集合时,键名就会变成如下:
map.put("username",参数值1);
map.put("arg1",参数值2);
map.put("param1",参数值1);
map.put("param2",参数值2);
- 在
UserMapper.xml
映射配置文件中定义SQL
xml <select id="select" resultType="user"> select * from tb_user where username=#{username} and password=#{param2} </select>
- 运行程序结果没有报错。而如果将
#{}
中的username
还是写成arg0
xml <select id="select" resultType="user"> select * from tb_user where username=#{arg0} and password=#{param2} </select>
运行程序则可以看到错误