3.Mybatis复习
3.1 xml配置
-
properties:加载配置文件
-
settings:设置驼峰映射
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
-
typeAliases:类型别名设置
#这样在映射器里面的resultType属性不需要写实体类的全路径名 <typeAliases> <package name="com.sh.pojo"/> </typeAliases>
-
mappers 映射器
-
加载接口,关联映射文件
<mappers> <package name="com.itheima.sh.dao"/> </mappers>
-
3.2 getMapper底层
- 首先调用Proxy.newProxyInstance方法创建Mapper动态代理
- 动态代理invoke方法里会传入代理对象的方法,通过method.getName()获得方法名,接着使用Xpath解析xml从而获得id=方法名的标签表达式
- 解析resultType属性值,通过反射技术class.forName(“resultType属性值”)获取到Class对象,通过newInstance()构造返回对象
- 解析出sql语句,执行,并且查询结果封装到返回对象中
3.3 SQL语句的CRUD
-
select
<!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名--> <select id="queryById" resultType="user" parameterType="int"> select * from user where id = #{id} </select>
-
insert
<insert id="saveUser"> insert into user values (null ,#{username},#{birthday},#{sex},#{address}) </insert>
-
update
<update id="updateUser"> update user set username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} where id = #{id} </update>
-
delete
<!--删除--> <delete id="deleteUser" > delete from user where id = #{id} </delete>
3.4 主键自增
新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值。
userMapper.saveUser(user);
//查看新的数据的主键值
System.out.println(user.getId());//null
使用insert标签的属性useGeneratedKeys,keyProperty,keyColumn实现:
属性 | 说明 |
---|---|
useGeneratedKeys | true 获取自动生成的主键,相当于select last_insert_id() |
keyColumn | 表中主键的列名 |
keyProperty | 实体类中主键的属性名 |
<insert id="saveUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user values (null ,#{username},#{birthday},#{sex},#{address})
</insert>
3.5 传入多个参数
步骤一 使用@Param注解注定名称:
//根据用户名和性别查询
User queryByUserNameAndSex(@Param("username") String userName, @Param("sex") String sex);
步骤二 在接收参数时,通过指定的名称获取参数值:
<select id="queryByUserNameAndSex" resultType="User">
select * from user where username=#{username} and sex=#{sex}
</select>
3.6 Pojo传参
接口与xml:
void saveUser(User user);
<insert id="saveUser">
insert into user values (null ,#{username},#{birthday},#{sex},#{address})
</insert>
底层: sql语句中 #{username}取值 -> 到pojo中调用 getUsername(){}
3.7 resultMap
resultMap标签的作用:自定义结果集,自行设置结果集的封装方式
- 配置resultMap
id属性:resultMap标签的唯一标识,不能重复,一般是用来被引用的
type属性:结果集的封装类型
autoMapping属性:操作单表时,不配置默认为true,如果pojo对象中的属性名称和表中字段名称相同,则自动映射。
<resultMap id="userResultMap" type="User" autoMapping="true">
<!--配置主键映射关系-->
<id column="id" property="id"></id>
<!--配置用户名的映射关系 column 表示数据表列 property表示pojo的属性-->
<result column="name" property="username"></result>
</resultMap>
-
修改select标签的statement中的resultMap
<select id="queryById" resultMap="userResultMap"> select * from user where id = #{id} </select>
4.动态SQL,多表查询,注解开发
4.1 基本动态SQL标签
-
if标签
<if test="判断条件"> 满足条件执行的代码 </if>
-
choose,when,otherwise标签
choose标签:分支选择(多选一,遇到成立的条件即停止) when子标签:编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行。 test属性:编写ognl表达式 otherwise子标签:当所有条件都不满足时,才会执行该条件。
可能优点乱,那来举个例子:
<!-- 根据用户名或者住址查询所有男性用户: 如果输入了用户名则按照用户名模糊查找, 否则就按照住址查找,两个条件只能成立一个, 如果都不输入就查找用户名为“孙悟空”的用户。 --> <select id="queryByUserNameOrAddress" resultType="user"> select * from user where sex='男' <choose> <when test="userName!=null and userName.trim()!=''"> and username like '%${userName}%' </when> <when test="address!=null and address.trim()!=''"> and address = #{address} </when> <otherwise> and username='孙悟空' </otherwise> </choose> </select>
-
where标签
标签作用:用于拼接多选一或者同时成立的SQL情况;
还会根据情况,动态的去掉SQL语句中的AND或者or;
还是举个例子吧:
需求: 如果输入了用户名按照用户名进行查询,如果输入住址,按住址进行查询,如果两者都输入,两个条件都要成立
假如不使用where标签,select标签应该这样:
<select id="queryByUserNameAndAge" resultType="user"> SELECT * FROM user where <if test="userName != null and userName.trim()!=''"> username = #{userName} </if> <if test="address!=null and address.trim()!=''"> AND address = #{address} </if> </select>
但是,假设用户名username是空,那么用户名的sql语句不参与条件,此时sql语句就会变为:SELECT * FROM user where AND address = #{address}
那么可以使用where关键字:
<select id="queryByUserNameAndAge" resultType="user"> SELECT * FROM user <where> <if test="userName != null and userName.trim()!=''"> username = #{userName} </if> <if test="address!=null and address.trim()!=''"> AND address = #{address} </if> </where> </select>
-
set标签
set标签:在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。
案例:修改用户信息,如果参数user中的某个属性为null,则不修改。
因此,set解决了这个问题:
<!--选择性地对user数据进行修改--> <update id="updateSelectiveUser"> update user <set> <if test="username != null and username.trim()!=''"> username = #{username}, </if> <if test="birthday != null"> birthday=#{birthday}, </if> <if test="sex != null and sex.trim()!=''"> sex=#{sex}, </if> <if test="address != null and address.trim()!=''"> address=#{address} </if> </set> where id = #{id} </update>
-
foreach标签
主要用来遍历:
<foreach collection="集合名或者数组名" item="元素" separator="标签分隔符" open="以什么开始" close="以什么结束"> #{元素} </foreach>
例子:
按照id值是1,2,3来查询用户数据
<!--根据多个id值查询--> <select id="queryByIds" resultType="user"> SELECT * FROM user WHERE id IN <foreach collection="arrIds" item="ID" separator="," open="(" close=")"> #{ID} </foreach> </select>
注意!!!在mapper接口编写传参时一定要加上@Param(“arrIds”)!!!!
4.2 一对多查询
以下例子的表关系如下:
假如我们要查询用户id为1的订单信息,用户(1)->订单(n),sql语句为:
select * from tb_user tbu inner join tb_order tbo on tbu.id = tbo.user_id where tbu.id=1
一个用户关联多个订单 User(List orderList) ,在User类中定义一个List集合存储多个订单Order对象。
编写XML文件:
<!--自定义结果集-->
<resultMap id="oneToManyResult" type="User" autoMapping="true">
<!--User的主键-->
<id column="uid" property="id"/>
<!--Order关联映射-->
<!--
1.一对多使用collection子标签进行关联多方Order
2.属性:
1)property="orders" 这里的orders表示User类的成员变量orders
2)javaType="List" 表示User类的成员变量orders存储的Order对象使用的类型,这里是List,可以不配置
3) ofType="Order" 表示List集合中存储数据的类型 Order
-->
<collection property="orders" javaType="List" ofType="Order" autoMapping="true">
<!--Order的主键-->
<id column="oid" property="id" />
</collection>
</resultMap>
<!--根据用户ID查询用户及其订单数据-->
<select id="oneToManyQuery" resultMap="oneToManyResult">
SELECT
tbo.id as oid,
tbo.order_number,
tbu.id as uid,
tbu.user_name,
tbu.password,
tbu.name,
tbu.age,
tbu.sex
FROM
tb_user tbu
INNER JOIN tb_order tbo ON tbu.id = tbo.user_id
WHERE
tbu.id = #{id}
</select>
总结下:
一对多关系配置:
1、在对象中添加映射关系;
2、编写接口方法,编写SQL;
3、编写resultMap处理数据库字段和实体类之间数据的封装;
4.3 多对多查询
举个例子:查询订单号为20140921001的订单的详情信息即查询订单信息+订单中的商品信息;
订单表(n)->订单商品中间表(1)<-商品表(m) SQL大概这样:
# 【需求】:查询订单号为20140921001的订单的详情信息 订单的详情信息 = 订单+商品
SELECT
*
FROM
tb_order tbo
INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
INNER JOIN tb_item item ON detail.item_id = item.id
WHERE
tbo.order_number = '20140921001';
那具体应该怎么实现呢?
首秀按,对实体类进行修改,一个订单表中关联了多个订单详情信息,所以在订单表中添加List<Orderdetail>
属性;每一条订单详情记录中都包含了一条商品信息,所以需要在Orderdetail中添加一个Item属性;
编写xml:
<!--订单及订单详情结果集-->
<resultMap id="orderAndDetailMap" type="Order" autoMapping="true">
<!--tb_order表 和 Order实体类-->
<!--订单表主键-->
<id property="id" column="oid"/>
<!--多个订单详情 1对多:detailList-->
<collection property="detailList" javaType="List" ofType="Orderdetail" autoMapping="true">
<!--tb_order_detail表 和 Orderdetail实体类-->
<!--订单详情主键 detailId表示下面sql语句的别名-->
<id property="id" column="detailId"/>
<!--关联商品对象 一对一:orderdetail-Item-->
<association property="item" javaType="Item" autoMapping="true">
<!--tb_item表 和 Item实体类 itemId 表示下面的sql语句别名-->
<id property="id" column="itemId"/>
</association>
</collection>
</resultMap>
<!--多对多查询-->
<select id="queryOrderAndDetailByOrderNumber" resultMap="orderAndDetailMap">
SELECT
tbo.id as oid,
tbo.order_number,
detail.id as detailId,
detail.total_price,
detail.status,
item.id as itemId,
item.item_detail,
item.item_name,
item.item_price
FROM
tb_order tbo
INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
INNER JOIN tb_item item ON detail.item_id = item.id
WHERE
tbo.order_number = #{orderNumber};
</select>
4.4 高级查询总结
resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
resultMap:
属性:
type 结果集对应的数据类型 Order
id 唯一标识,被引用的时候,进行指定
autoMapping 开启自动映射
extends 继承
子标签:
id:配置id属性
result:配置其他属性
association:配置一对一的映射
property 定义对象的属性名
javaType 属性的类型
autoMapping 开启自动映射
collection:配置一对多的映射
property 定义对象的属性名
javaType 集合的类型
ofType 集合中的元素类型 泛型
autoMapping 开启自动映射
4.5 注解开发
@Insert:保存
Value:sql语句(和xml的配置方式一模一样)
@Update:更新
Value:sql语句
@Delete: 删除
Value:sql语句
@Select: 查询
Value:sql语句
@Options:可选配置(主键回填)
userGeneratedKeys:开关,值为true表示可以获取主键 相当于select last_insert_id()
keyProperty :对象属性
keyColumn : 列名