参数为java bean 或多个参数时 Mybatis是怎样封装数据的?
接口方法是多个参数时,推荐使用注解
MyBatis封装这些参数为Map时,键应该是什么??
MyBatis底层肯定是用反射来进行的这些操作,那么反射可以获取到方法,但是是否能获取的方法的参数名称呢?
答案是不行,方法的参数名称是形参,是可变的。反射类Method中,并没有提供获取方法名称的功能。所以MyBatis并不知道我们传递的参数名称,只知道这些参数的值!
再次回到上面的问题,只有值,那么MyBatis封装Map的时候,应该以什么做键呢?
答案是这样的:默认情况下MyBatis会设置两种键:
A:以从0开始的递增数字作为键,第一个参数是0,第2个参数就是1,以此类推
B:以”param” + i 作为键,i是从1递增的数字,第一个参数键就是param1,第2个就是param2,以此类推
所以我们可以通过#{0} 或 #{param1} 来取到第一个参数,以此类推
#{ } 和 ${ }的用法
两者都支持@param注解, 指定参数名称, 获取参数值. 推荐这种方式
#是占位符, 会对SQL进行预编译,相当于?; $是做sql拼接, 有SQL注入的隐患
#不需要关注数据类型, MyBatis自动实现数据类型转换; ${} 必须自己判断数据类型
一般做参数传递,都会使用#{}
如果不是做预编译,而是做拼接sql, 会使用${}, 例如表名称的变化
ResultMap的高级映射
解决属性名和列名不一致的问题
<settings>
<!--是否开启驼峰标识-->
<setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
<resultMap id="userResultMap" type="User">
<!--
property: javabean对应的属性
column : 数据库对应的列名
-->
<id property="id" column="id"></id>
<result property="userName" column="user_name"/>
</resultMap>
<select id="queryUserById" resultMap="userResultMap">
select * from tb_user where id=${id}
</select>
autoMapping自动映射
刚才的配置中,只配置了id和userName字段,其它字段没有配置,映射也没有问题,为什么呢?
这是因为,在resultMap中,有一个属性叫做:autoMapping,如果值为true,并且列名称和字段名一致,是可以完成自动映射的。
默认情况下,这个autoMapping的值就是为true的。
因此,一般我们配置resultMap,只要字段符合规则,我们只需要把ID配置出来就OK了。其它字段可以自动映射!
自动映射策略
Mybatis的自动映射策略默认是开启的,而且默认是只对非嵌套的resultMap进行自动映射。这是通过Mybatis的全局配置autoMappingBehavior参数配置的。它一共有三种取值,分别是NONE、PARTIAL和FULL。
l NONE表示不启用自动映射
l PARTIAL表示只对非嵌套的resultMap进行自动映射
l FULL表示对所有的resultMap都进行自动映射
全局配置文件<setting name="autoMappingBehavior" value="PARTIAL"/>
除了全局的是否启用自动映射的配置外,还可以对特定的resultMap设置是否启用自动映射。这是通过resultMap的autoMapping属性配置的,可选值是true和false。定义在resultMap上的autoMapping的优先级比全局配置的优先级更高。
SQL片段
<!--
定义sql片段
id: sql片段的唯一标识
-->
<sql id="userColumns">
id, user_name, password, name, age, sex, birthday, created, updated
</sql>
<select id="queryUserById" resultMap="userResultMap">
select <include refid="userColumns"/>
from tb_user where id=${id}
</select>
<select id="queryUserList" resultType="User">
select <include refid="userColumns"/>
from tb_user
</select>
动态sql
需求1:查询所有男性用户,如果输入了姓名,则按照姓名模糊查找;如果没有输入则不管姓名
需求2:查询所有用户,传递参数orderType,如果值为0,按照年龄升序排序,如果为1则按照年龄降序排序,否则按照ID排序
需求3:查询所有用户,如果有姓名不为空,则按照姓名模糊查找;如果年龄也不为空,则还要满足年龄条件。
需求4:修改用户信息,如果某字段为null,则不修改这个字段
需求5:根据多个ID查询用户
<?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.hrh.mapper.UserMapper">
<sql id="*">
id,user_name,password,name,age,sex,birthday,created,updated
</sql>
<!--Id回填-->
<insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into tb_user (<include refid="*"/>)
VALUES (null,#{userName},#{password},#{name},#{age},#{sex},#{birthday},now(),now());
</insert>
<select id="queryByUP" resultType="User">
select * from tb_user where user_name=#{username} and password=#{password};
</select>
<select id="queryById" resultType="User">
select * from tb_user where id=${id};
</select>
<resultMap id="userResultMap" type="User">
<id property="id" column="id"></id>
<result property="userName" column="user_name"/>
</resultMap>
<select id="queryUserById" resultMap="userResultMap">
select * from tb_user where id=#{id}
</select>
<!--查询所有男性用户,如果输入了姓名,则按照姓名模糊查找;如果没有输入则不管姓名-->
<select id="query1" resultMap="userResultMap">
select * from tb_user where sex=1
<if test="name!=null and name.trim()!=''">
and name like '%${name}%'
</if>
</select>
<!--查询所有用户,传递参数orderType,如果值为0,按照年龄升序排序,
如果为1则按照年龄降序排序,否则按照ID排序-->
<select id="query2" resultMap="userResultMap">
select * from tb_user
<choose>
<when test="orderType==0">order by age ASC </when>
<when test="orderType==1">order by age desc </when>
<otherwise>order by id asc</otherwise>
</choose>
</select>
<!--查询所有用户,如果有姓名不为空,则按照姓名模糊查找;
如果年龄也不为空,则还要满足年龄小于指定年龄。-->
<select id="query3" resultMap="userResultMap">
select * from tb_user
<where>
<if test="name!=null and name.trim()!=''">
and name like '%${name}%'
</if>
<if test="age!=null">
and age < ${age}
</if>
</where>
</select>
<!--修改用户信息,如果某字段为null,则不修改这个字段-->
<update id="update1">
update tb_user
<set>
<if test="userName!=null and userName.trim()!=''">
user_name = #{userName},
</if>
<if test="password!=null and password.trim()!=''">
password = #{password},
</if>
<if test="name!=null and name.trim()!=''">
name = #{name},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="sex!=null">
sex = #{sex},
</if>
<if test="birthday!=null">
birthday = #{birthday},
</if>
updated = now()
</set>
where id=#{id}
</update>
<select id="query4" resultMap="userResultMap">
select * from tb_user
<where>
<if test="ids!=null and ids.size()!=0">
and id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
</mapper>
package com.hrh.mapper;
import com.hrh.domain.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
//Id回显
public void insertUser(User user);
//用户名和密码查找用户
public User queryByUP(@Param("username") String username,@Param("password") String password);
//${}
public User queryById(@Param("id") Long id);
//resultMap 解决字段名不一致
public User queryUserById(@Param("id")Long id);
//动态sql if
public List<User> query1(@Param("name") String name);
//动态sql choose when otherwise
public List<User> query2(@Param("orderType") Integer orderType);
//动态sql where
public List<User> query3(@Param("name") String name, @Param("age") Integer age);
//动态sql set
public void update1(User user);
//动态sql foreach
public List<User> query4(@Param("ids") List<Long> ids);
}
package com.hrh.mapper;
import com.hrh.domain.User;
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 org.junit.Before;
import org.junit.Test;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.*;
public class UserMapperTest {
UserMapper mapper;
@Before
public void setUp() throws Exception {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
mapper=sqlSession.getMapper(UserMapper.class);
}
@Test
public void insertUser() throws Exception {
User user=new User();
user.setName("嘻嘻嘻");
mapper.insertUser(user);
System.out.println(user.getId());
}
@Test
public void queryByUP() throws Exception {
User user = mapper.queryByUP("zhangsan", "123456");
System.out.println(user);
}
@Test
public void queryById() throws Exception {
User user = mapper.queryById(1L);
System.out.println(user);
}
@Test
public void queryUserById() throws Exception {
User user = mapper.queryUserById(2L);
System.out.println(user);
}
@Test
public void query1() throws Exception {
List<User> users = mapper.query1(null);
for (User user : users) {
System.out.println(user);
}
}
@Test
public void query2() throws Exception {
List<User> users = mapper.query2(1);
for (User user : users) {
System.out.println(user);
}
}
@Test
public void query3() throws Exception {
List<User> users = mapper.query3("张",56);
for (User user : users) {
System.out.println(user);
}
}
@Test
public void update1() throws Exception {
User user = mapper.queryById(1L);
user.setAge(888);
mapper.update1(user);
}
@Test
public void query4() throws Exception {
ArrayList<Long> list = new ArrayList<>();
Collections.addAll(list,new Long[]{1L,2L,3L,4L});
List<User> users = mapper.query4(list);
for (User user : users) {
System.out.println(user);
}
}
}
缓存
为数据库的查询进行缓存,是减少数据库压力的主要途径。分为一级缓存和二级缓存
一级缓存:session级别缓存,作用于当前会话。
二级缓存:SessionFactory级别缓存,作用于整个SessionFactory,多个会话之间可以共享缓存
一级缓存
特点:
mybatis的一级缓存默认就是开启的,并且无法关闭。
mybatis的一级缓存作用域是当前session,一次openSession()后,如果相同的statement和相同参数,则不进行查询而是从缓存命中并且返回,如果没有命中则查询数据库。
任何的增删改操作都会导致缓存被清空
缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回
二级缓存
特点:
二级缓存需要手动开启,开启的方式是在Mapper.xml中添加标签:<cache/>
mybatis-config.xml全局配置文件中,有一个配置,是二级缓存的全局开关,如果关闭,所有Mapper的二级缓存都会失效,默认是打开的。
二级缓存的作用域是整个SessionFactory,并且是同一个Mapper中,如果namespace、statement和SQL参数一致,则缓存命中
开启二级缓存后, 查询的对象需要实现序列化接口.
二级缓存的其他配置
所有的这些属性都可以通过缓存元素的属性来修改。比如:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。
可用的收回策略有:
LRU
– 最近最少使用的:移除最长时间不被使用的对象。
FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。
SOFT
– 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK
– 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。默认的是 LRU。
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
高级查询
一对一查询:一个订单只属于一个用户
需求1:根据订单号 查询订单的同时,查询出订单所属用户
一对多查询:一个订单可以有多个订单详情
需求2:根据订单号 查询订单,并且查询出所有订单详情及所属用户
多对多查询:订单中可以有多个商品,商品也可以属于多个订单
需求3:根据订单号 查询订单,查询出所属用户,并且查询出订单的详情,及订单详情中的对应的商品信息
OrderMapper接口
package com.hrh.mapper;
import com.hrh.domain.Order;
import org.apache.ibatis.annotations.Param;
public interface OrderMapper {
//需求1 两表查询
public Order queryTwo(@Param("orderId")String orderId);
//需求2 三表查询
public Order queryThree(@Param("orderId")String orderId);
//需求3 四表查询
public Order queryFour(@Param("orderId")String orderId);
}
OrderMapper.xml 配置文件 ( 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">
<mapper namespace="com.hrh.mapper.OrderMapper">
<resultMap id="queryTwoResultMap" type="Order" autoMapping="true">
<id property="oid" column="oid"/>
<association property="user" javaType="User" autoMapping="true">
<id property="id" column="user_id"/>
</association>
</resultMap>
<resultMap id="queryThreeResultMap" type="Order" autoMapping="true" extends="queryTwoResultMap">
<collection property="orderitemList" javaType="list" ofType="Orderitem" autoMapping="true">
<id column="item_id" property="itemId"/>
</collection>
</resultMap>
<resultMap id="queryFourResultMap" type="Order" autoMapping="true">
<id property="oid" column="oid"/>
<association property="user" javaType="User" autoMapping="true">
<id column="id" property="id" />
</association>
<collection property="orderitemList" javaType="list" ofType="Orderitem" autoMapping="true">
<id property="itemId" column="item_id"/>
<association property="product" javaType="Product" autoMapping="true">
<id property="pid" column="pid"/>
</association>
</collection>
</resultMap>
<!--根据订单号 查询订单的同时,查询出订单所属用户-->
<select id="queryTwo" resultMap="queryTwoResultMap">
SELECT * FROM tb_order o
LEFT JOIN tb_user u ON o.user_id=u.id
where o.order_number=#{orderId}
</select>
<!--根据订单号 查询订单,并且查询出所有订单详情及所属用户-->
<select id="queryThree" resultMap="queryThreeResultMap">
SELECT * FROM tb_order o
LEFT JOIN tb_user u ON o.user_id=u.id
LEFT JOIN tb_orderitem oi ON oi.order_id=o.oid
WHERE o.order_number=#{orderId}
</select>
<!--根据订单号 查询订单,查询出所属用户,并且查询出订单的详情,及订单详情中的对应的商品信息-->
<select id="queryFour" resultMap="queryFourResultMap">
SELECT * FROM tb_order o
LEFT JOIN tb_user u ON o.user_id=u.id
LEFT JOIN tb_orderitem oi ON oi.order_id=o.oid
LEFT JOIN tb_product p ON p.pid=oi.product_id
WHERE o.order_number='20140921001'
</select>
</mapper>
接口测试类
package com.hrh.mapper;
import com.hrh.domain.Order;
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 org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import static org.junit.Assert.*;
public class OrderMapperTest {
OrderMapper orderMapper;
@Before
public void setUp() throws Exception {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
orderMapper=sqlSession.getMapper(OrderMapper.class);
}
@Test
public void queryTwo() throws Exception {
Order order = orderMapper.queryTwo("20140921003");
System.out.println(order);
}
@Test
public void queryThree() throws Exception {
Order order = orderMapper.queryThree("20140921001");
System.out.println(order);
}
@Test
public void queryFour() throws Exception {
Order order = orderMapper.queryFour("20140921001");
System.out.println(order);
}
}