道阻且长,行则将至。
目录
- 一、Mapper概述
- 1.Mapper代理开发
- 2.Mapper开发流程
- 准备
- 实现查询
- 二、Mybatis-CRUD
- 0.编辑类Brand
- 1.查询所有数据
- 2.根据id查询
- 3.多条件查询
- 4.动态单个条件查询
- 5.添加数据
- 三、注解实现CRUD
一、Mapper概述
通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。
参考gitee:MyBatis 通用 Mapper4
1.Mapper代理开发
之前我们写的Mybatis代码是基本使用方式,【参考Mybatis快速入门】,它存在硬编码的问题,例如执行sql:sqlSession.selectList("test.selectAll");
,传递的字符串参数是映射配置文件中的 namespace.id值,这样写不便于后期的维护。如果使用 Mapper 代理方式则不存在硬编码问题。
2.Mapper开发流程
准备
新建一个Maven项目:
一个module已建立好:
在main的java里面创建好你的包mybatis,在这个包下面可以再建立2个包,一个存放你的mapper接口,一个创建一个类来存储我们后面用于接收数据。同时在resource里面建立一个和mapper同样结构的文件目录,里面存放SQL映射文件,这个文件名称要和mapper接口一致。
然后在mapper里面新建一个接口,查询所有数据的例子:
package mybatis.mapper;import mybatis.pojp.Brand;import java.util.List;public interface BrandMapper { List<Brand> selectAll();}
继续编写我们的mapper xml文件,也就是SQL映射文件。namespace
是对应接口相对于source的目录+名称, resultType
是对应类的目录名称,当在mybatis的配置文件中设置好给类起别名映射,就也可以直接不区分大小写写类名了。
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="mybatis.mapper.BrandMapper"> <select id="selectAll" resultType="brand"> select * from tb_brand; </select></mapper>
<typeAliases> <package name="mybatis.pojp"/> </typeAliases>
至此mapper的前置文件部分准备好了。
如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载。也就是将核心配置文件的加载映射配置文件的配置修改为:
<mappers> <!-- --> <package name="com.itheima.mapper"/></mappers>
实现查询
我们可以在test里面建立我们测试项目:mybatis/test/MyBatisTest.java
。使用了代理mapper之后我们执行SQL的代码就从字符串参数”test.selectAll”变为UserMapper.class了。
sqlSession.selectList("test.selectAll");
—>sqlSession.getMapper(UserMapper.class);
public static void main(String[] args) throws IOException { //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象,用它来执行sql SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 执行sql //3.1 获取UserMapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.selectAll(); System.out.println(users); //4. 释放资源 sqlSession.close();}
二、Mybatis-CRUD
这一节我们使用映射配置文件实现CRUD操作,也就是增删改查操作。
create table tb_brand( id int primary key auto_increment, brand_name varchar(20), company_name varchar(20), ordered int, description varchar(100), -- 状态:0:禁用 1:启用 status int);
继续上一节的内容,我们操作一个这样的数据表。
0.编辑类Brand
上一节我们在pojp
包下创建了 Brand 实体类。我们根据数据表编辑对应的参数:
public class Brand { private Integer id; private String brandName; private String companyName; private Integer ordered; private String description; private Integer status;}
1.查询所有数据
- 编写接口方法:
List<Brand> selectAll();
- 编写SQL
<select id="selectAll" resultType="brand"> select * from tb_brand; </select>
- 测试代码
在测试项目mybatis/test/MyBatisTest.java
中开始我们的测试代码。我们可以使用@Test注解。
@Test public void testSelectAll() throws IOException { //1. 获取SqlSessionFactory String resource = "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(); }
成功运行测试:
写到这里,我们会发现使用mapper代理后面只需要操作三个文件内容,
其实在测试方法里面我们需要修改的也只是执行方法,在后面会有具体体现。
但是在这里我们发现一个问题,就是打印出来的内容有一些是null
,但是数据库里面的表是存在数据的。还有注意是不是写了tostring
。从打印结果可以看到 brandName
和 companyName
这两个属性的数据没有封装成功,查询 实体类 和 表中的字段 发现,是因为他们的名称不一致,这个问题可以通过两种方式进行解决:
1.给字段起别名;
2.使用resultMap定义字段和属性的映射关系。
- 起别名
写sql语句时给这两个字段起别名,将别名定义成和属性名一致:
<select id="selectAll" resultType="brand"> select id, brand_name as brandName, company_name as companyName, ordered, description, status from tb_brand;</select>
SQL语句中一大串的内容很麻烦也不美观,Mybatis提供了sql
片段可以提高sql的复用性。id属性值是唯一标识,后面通过该值进行引用。
<sql id="brand_column">id, brand_name as brandName, company_name as companyName, ordered, description, status</sql>
原sql语句引用 id="selectAll"
:
<select id="selectAll" resultType="brand"> select <include refid="brand_column" /> from tb_brand;</select>
- 映射
使用resultMap定义字段和属性(不一致)的映射关系。
<resultMap id="brandResultMap" type="brand"> <result column="brand_name" property="brandName"/> <result column="company_name" property="companyName"/></resultMap>
sql语句的resultType
改为resultMap
并引用其id。
<select id="selectAll" resultMap="brandResultMap"> select * from tb_brand;</select>
2.根据id查询
- 编写接口方法
Brand selectById(int id);
- 编写SQL语句
这里是根据id来查询,所以就有了参数的传入。就需要用到参数占位符,参数占位符在sql中就是?
mybatis提供了两种参数占位符:
1. #{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。
2. ${} :拼接SQL。底层使用的是Statement
,会存在SQL注入问题。
编写如下的SQL语句:
<select id="selectById" resultMap="brandResultMap"> select * from tb_brand where id = #{id};</select>
- 测试代码
@Testpublic void testSelectById() throws IOException { //接收参数id int id = 1; //1. 获取SqlSessionFactory String resource = "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.selectById(id); System.out.println(brand); //5. 释放资源 sqlSession.close();}
是不是测试代码就是修改了对应执行方法。
- 特殊字段处理
映射配置文件是xml
类型的问题,而>
<
等字符在xml中有特殊含义,所以这些符号需要转义,有两种方式进行转义:转义字符、CDATA区。
3.多条件查询
使用条件查询返回多个结果,所以我们需要使用list来存储。还需要考虑如何实现条件。
- 编写接口方法
我们需要传多个参数,所以这里也有多个写法:
1、使用 @Param("参数名称")
标记每一个参数,在编写SQL时使用 #{参数名称}
进行占位
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName );
2、将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。在SQL中使用 #{内容}
,内容
和实体类属性名一致。
List<Brand> selectByCondition(Brand brand);
3、将多个参数封装到map集合中,将map集合作为接口的方法参数。在SQL中使用 #{内容}
,内容
和map集合中键的名称一致。
List<Brand> selectByCondition(Map map);
- SQL语句
<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() throws IOException { //接收参数 int status = 1; String companyName = "华为"; String brandName = "华为"; // 处理参数 companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%"; //1. 获取SqlSessionFactory String resource = "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. 执行方法 3kind //方式一 :接口方法参数使用 @Param 方式调用的方法// List brands = brandMapper.selectByCondition(status,companyName,brandName); //方式二 :接口方法参数是 实体类对象 方式调用的方法/* Brand brand = new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName); List 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); //5. 释放资源 sqlSession.close(); }
- 动态SQL
使用动态SQL可以解决输入条件时,不需要填写全部条件,自动根据输入内容进行匹配。
我们可以利用mybatis提供的if 标签和 where 标签:
【if标签】
可以判断是不是存在对于参数,进行sql的拼接。
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where <if test="status != null"> 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>
这样的情况又会出现问题:如果第一个参数没有,那么首位出现and
,SQL语句肯定报错。
【where标签】
where标签就是解决这一问题的,会自动去掉非法的and
,如果没有参数则不加where
关键字。第一个条件也写了and。
<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>
4.动态单个条件查询
- 编写接口方法
List selectByConditionSingle(Brand brand);
- SQL语句
为了实现动态查询我们使用 choose(when,otherwise)
标签 , choose
标签相当于Java 的switch。
<select id="selectByConditionSingle" resultMap="brandResultMap"> select * from tb_brand <where> <choose> <when test="status != null"> status = #{status} </when> <when test="companyName != null and companyName != '' "> company_name like #{companyName} </when> <when test="brandName != null and brandName != ''"> brand_name like #{brandName} </when> </choose> </where></select>
- 测试代码
@Testpublic void testSelectByConditionSingle() throws IOException { //接收参数 int status = 1; String companyName = "华为"; String brandName = "华为"; // 处理参数 companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%"; //封装对象 Brand brand = new Brand(); //brand.setStatus(status); brand.setCompanyName(companyName); //brand.setBrandName(brandName); //1. 获取SqlSessionFactory String resource = "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.selectByConditionSingle(brand); System.out.println(brands); //5. 释放资源 sqlSession.close();}
5.添加数据
编写接口方法
void add(Brand brand);
SQL语句
<insert id="add"> insert into tb_brand (brand_name, company_name, ordered, description, status) values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});</insert>
- 测试代码
@Testpublic void testAdd() throws IOException { //接收参数 int status = 1; String companyName = "AA科技"; String brandName = "AA"; String description = "AA创新生活!"; int ordered = 100; //封装对象 Brand brand = new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); //1. 获取SqlSessionFactory String resource = "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. 执行方法 brandMapper.add(brand); //提交事务 sqlSession.commit(); //5. 释放资源 sqlSession.close();}
是不是发现大多是一个套路!都是类似的。
三、注解实现CRUD
使用注解开发会比配置文件开发更加方便。
Mybatis 针对 CURD 操作都提供了对应的注解:
- 查询 :@Select
- 添加 :@Insert
- 修改 :@Update
- 删除 :@Delete
例如在接口中就可以这样写,不需要处理配置文件了:
@Select(value = "select * from tb_user where id = #{id}")public User select(int id);
只是注解适用于完成简单功能,配置文件可以处理复杂功能。
1.使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂-点的语句,JAVA注解不仅力不从心,还会让你本就复杂的SQL语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用xml来映射语句。
2.选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队.换句话说,永远不要拘泥于种方式,你可以很轻松的在基于注解和xml的语句映射方式间自由移植和切换。
end
☕物有本末,事有终始,知所先后。
☝☝☝☝☝我的CSDN☝☝☝☝☝☝