MyBatis优秀的持久层框架,用于简化JDBC开发
MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
一些持久层框架
持久层:
- 负责将数据保存到数据库,操作数据库数据的那一层代码
- javaee三成架构 :表现层,业务层,持久层
框架
- 框架是一个半成品软件,一套可重用的,通用的,软件基础代码模型
- 在框架的基础之上构建软件编写更加高效,规范,通用,可扩展
JDBC 的缺点
JDBC代码
- 硬编码
- 一些语句的改动不方便等等
- 注册驱动,获取连接,SQL语句处的硬编码
- 操作繁琐
- 手动操作参数
- 手动封装结果集 等等
MyBatis 入门
入门操构建一个查询所有的MyBatis模块
1创建数据库表单
2 安装
要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency>
3 编写MyBatis核心配置文件→替换连接信息解决硬编码问题
→→→→→→→→→→→→
<--指定sql映射配置文件的历经--> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers>
4编写SQL映射文件→统一管理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:名称空间 配合要执行的sql语句标签中的id使用
id是sql语句的唯一标识,resultType是返回值类型 --> <mapper namespace="com.yang.mapper.UserMapper"> <select id="selectAll" resultType="com.yang.Pojo.User"> SELECT * FROM tb_user </select> </mapper>
5编码
- 设计pojo javabean封装类
- 加载核心配置文件,获取SqlSessionFactory对象
- 获得SqlSession对象,执行Sql语句
- 释放资源
//加载核心配置文件,获取SqlsessionFactory工厂对象 InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); //获取sqlSession对象 SqlSession sqlSession = build.openSession(); //执行sql语句,并接受结果集 List<User> list = sqlSession.selectList("com.yang.mapper.UserMapper.selectAll");//mapper的名称空间.select的id属性值 sqlSession.close(); for (User user : list) { System.out.println(user); }
sql语句爆红是没有连接数据库
6 了解了原生的开发,只是为了让我们更好的了解Mybatis的原理,上面代码仍然存在硬编码问题
在我们传递sql语句映射地址的时候仍然有硬编码问题所以我们又要使用Mapper代理开发
我们只需要增加一个对应封装类的Mapper接口,如User的UserMapper接口,保证mapper的命名空间namespce为mapper接口的全类名。只需保证接口中的抽象方法返回值能接受你sql语句的返回值,方法名保持sql的唯一标识相同即可,这样我们的MyBatis即可利用反射原理,替我们完成老本版方法的连接,这样能让你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。但我们需要定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下。(编译后和对应皆苦的Class文件在同一地址下就可以)
我们使用UserMapper.class对象能够得到该接口的全限定名,又因为SQL映射配置文件也是这个地址,我们执行该接口的方法,改方法和SQL id一致,我们便可以执行SQL语句,且该方法的返回值,也是和之前sqlSession直接执行SQL语句一样的,所以者样我们使用Mapper代理便实现了旧版本的升级。
使用了Mapper代理后,我们可以使用包扫描的方法完成sql映射文件的加载
<mappers> <!--加载映射配置文件--> <!--<mapper resource="UserMapper.xml"/>--> <!-- 更换为包扫描--> <package name="com.yang.mapper"/> </mappers>
这样我们的项目结构就变成了这样
public static void main(String[] args) throws IOException { //加载核心配置文件,获取SqlsessionFactory工厂对象 InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream); //获取sqlSession对象 SqlSession sqlSession = build.openSession(); //执行sql语句,并接受结果集 //List<User> list = sqlSession.selectList("test.selectAll");//名称空间.id属性值 //使用mapper代理 获得UserMapper接口的代理对象 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectAll(); sqlSession.close(); //for遍历集合,处理结果 for (User user : users) { System.out.println(user); } }
7Mapper核心配置文件内容
官网很详细(配置遵守顺序就可以)
8注意
当数据库字段名,与实体类属性名称对应不上时,数据便不能自动封装,这会导致我们查询到结果为空
如 brand_name 与brandName
- sql映射文件中的sql语句利用as关键字起别名 brand_name as brandName
<select id="selectAll" resultType="Brand"> select brand_name as brandName from tb_brand; </select>
- 当很多sql语句需要起别名时,我们可以抽取sql片段 缺点不灵活
<sql id="brand_cloum"> brand_name as brandName </sql> <select id="selectAll" resultType="Brand> select <include refid="brand_cloum"/> from tb_brand; </select>
- 使用resultMap映射
<!--方案三:resultMap--> <!-- resultMap标签:手动配置数据表字段和实体类的映射关系 id属性:唯一标识 type属性:映射的实体类类型,支持别名 --> <resultMap id="brand_mapper" type="Brand"> <result column="brand_name" property="brandName"/> </resultMap> //resultType换为resultMap 值设为 resultMap映射ID <select id="selectAll" resultMap="brand_mapper"> select * from tb_brand; </select>
9参数占位符和xml中sql语句的特殊字符
<!-- 1.参数占位符 #{}:使用预编译,将参数替换为 ? 占位符。防止sql注入,安全! ${}:直接拼接sql语句,存在sql注入,不安全! 使用场景: #{}:实际参数传递时使用 ${}:表名或字段名不固定时使用 2.参数类型:parameterType属性(可以省略不写)这里parameterType的属性设置为 int 3.特殊字符处理:< > <>等等 转义字符替换:SELECT * FROM tb_brand WHERE id < #{id} CDATA区: SELECT * FROM tb_brand WHERE id <![CDATA[ < ]]> #{id} --> <select id="selectById" parameterType="int" resultMap="brandResultMap"> SELECT * FROM tb_brand WHERE id = #{id} </select>
<select id="selectById" resultMap="brand_mapper">
select * from tb_brand where id <![CDATA[
<
]]>#{num};
</select>
10 Mapper 映射接口中的sql方法多参数接收
为保证我们sql映射文件中的sql语句的#{}里面的参数占位符,能与我们在Mapper接口中对应方法的形参对应起来我们可以使用三种方法
<select id="selectByCodition" resultMap="brand_mapper"> 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>
- 散装参数使用Param注解 @Param("sql语句的参数占位符名称")
List<Brand> selectByCodition(@Param("status") int status, @Param("brandName") String brandName,@Param("companyName") String companyName);
- 实体类封装参数 要求sql语句的参数占位符与实体类的属性名一致就可以了,其他无需改动
List<Brand> selectByCodition(Brand brand);
- Map集合封装参数 sql语句占位符与Map 的 key保持一致
List<Brand> selectByCondition(Map map);
11 动态多条件查询
当我们需要多条件查询时我们会编写这样的sql语句
<select id="selectByCodition" resultMap="brand_mapper"> select * from tb_brand where status=#{status} and company_name like #{companyName}and brand_name like #{brandName} </select>
但是当用户只给一个company_name的值查询时,程序会报错
因为 会产生 where and sql语句的语法错误 可以在where后加恒等式 或者使用<where>标签 会帮我们去掉多余的and关键字
这时我们需要一个动态SQL语句,使得sql语句随着用户的输入或外部条件的变化而变化 。MyBatis为我们提供了很多sql标签来帮助我们完成动态SQL
<select id="selectByCodition" resultMap="brand_mapper"> 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>
12 动态单条件查询
Mabatis为我们提供了 <choose><when></when><when></when><otherwise></otherwise>动态单条件查询标签
choose相当于switch when相当于 case otherwise相当于 default
<select id="selectByConnditionSingle" resultType="com.yang.pojo.Brand"> select * from tb_brand <where> <choose> <when test="status!=null"> status=#{status} </when> <when test="brandName!=null and brandName!=''"> brand_name like #{brandName} </when> <when test="companyName!=null and companyName!=''"> company_name like #{companyName} </when> </choose> </where> </select>
13 提交事务
MaBatis事务:
- openSession(): 默认开启事务,进行增删改查操作后需要使用sqlSession.commit();手动提交事务
- openSession(true): 可以设置为自动提交事务(关闭事务)
//进行增删改 操作需要对事务进行设置
try { brandMapper.add(brand); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); }finally { sqlSession.close(); }
14 获取主键id
-
useGeneratedKeys属性:是否使用生成的主键
-
keyProperty属性:主键名称
<!--添加数据后、可以返回自增数据的主键--> <insert id="add" useGeneratedKeys="true" keyProperty="id"> INSERT INTO tb_brand (brand_name,company_name,ordered,description,status) VALUES (#{brandName},#{companyName},#{ordered},#{description},#{status}) </insert>
15 动态修改字段
添加<if></if>标签判断(防止其他字段被修改为null),sql中的,会出现错误,我们需要使用<set>标签
<!-- 动态修改数据 <set>标签:替换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>
16批量删除
<!--
批量删除
mybatis会将数组参数,封装为一个一个Map集合:
*默认 array=数组
*或者使用Param注解来改变Map集合的默认名称 <foreach>标签:循环 collection属性:获取容器的名称,数组默认是array,可以通过@Param()指定名称 item属性:接收每个元素的变量名 separator属性:连接符 open属性:开始符号 close属性:结束符号 --> <!--collection 数组或者集合名 item 数组元素 separater item之间的连接符 open 开始符号 close 结束符号--> <delete id="deleteByIds"> delete from tb_brand where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach>
;
</delete>
17 MyBatis 参数传递
-
MyBatis接口方法中的形参可以是多种类型的,MyBatis底层会对这些参数自动进行不同的封装
-
通过 ParamNameResolver 类进行封装参数
2.单个参数情况
-
POJO类型:直接使用,属性名 和 参数占位符名称 一致
-
Map集合:直接使用,键名 和 参数占位符名称 一致
-
Collection:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
-
map.put("arg0",collection集合);
-
map.put("collection",collection集合);
-
-
List:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
-
map.put("arg0",list集合);
-
map.put("collection",list集合);
-
map.put("list",list集合);
-
-
Array:封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
-
map.put("arg0",数组);
-
map.put("array",数组);
-
-
其他类型:直接使用
3.多个零散参数情况
-
封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
-
未使用@Param注解
-
map.put("arg0",参数值1)
-
map.put("param1",参数值1)
-
map.put("param2",参数值2)
-
map.put("agr1",参数值2)
-
-
使用@Param注解
-
map.put("username",参数值1)
-
map.put("param1",参数值1)
-
map.put("param2",参数值2)
-
map.put("agr1",参数值2)
-
4.建议
-
以后都使用@Param()注解来修改指定的键名替换arg名(param键名不会被替换),方便记忆参数和提高可读性
18 使用注解完成增删改查
-
在接口方法上,通过不同的注解,将SQL语句直接给定,可以省去映射配置文件的编写!
-
例如
@Select(value = "SELECT * FROM tb_brand WHERE id = #{id}") public Brand select(int id);