1. Myabtis参数的获取
在Mybatis中获取参数是非常常见的操作,无论是增删改查哪种操作,基本都会传入参数。
1.1 获取单个参数
单个参数:mybatis不会做特殊处理,
#{参数名/任意名}:取出参数值
1.2 获取多个参数
多个参数:mybatis会做特殊处理。
多个参数会被封装成 一个map
-
key:param1...paramN,或者参数的索引也可以
-
value:传入的参数值
#{}其实就是从map中获取指定的key的值;
-
命名参数
-
明确指定封装参数时map的key:比如@Param("id")
-
key:使用@Param注解指定的值
-
value:参数值
-
如:
/** * 根据性别和年龄 查询学生 */ List<Student> findStudentBySexAndAge(@Param("sex") String sex, @Param("age") int age);
1.3 获取Bean类型的参数
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
{属性名}:取出传入的pojo的属性值
比如添加一个用户的操作,那我们可以将一个用户对象当做参数传入,这样其实也是一个参数,获取的时候{}里面的参数名称 必须和对象的属性名一致
如:
接口中:
int addStduent(Student student);
映射文件中:
<!--添加学生-->
<insert id="addStduent" parameterType="s">
insert into student values (null,#{name},#{age},#{sex},#{join_time})
</insert>
测试文件中:
@Test
public void addStudentTest(){
Student student = new Student();
student.setName("李老板");
student.setAge(40);
student.setSex("男");
student.setJoin_time("2024-5-1");
int i = mapper.addStduent(student);
System.out.println(i);
}
1.4 获取集合参数
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便我们也可以传入map 获取的格式为
#{key}:取出map中对应的值
接口中:
/**
* 添加一个学生 要求参数类型是map集合
*/
int addStudentByMap(Map map);
映射文件中:
<!--添加学生-->
<insert id="addStudentByMap" parameterType="java.util.Map">
insert into student values (null,#{name},#{age},#{sex},#{join_time})
</insert>
测试文件中:
//添加学生map集合作为参数方式
@Test
public void addStudentByMapTest(){
Map<String,Object> map = new HashMap<String, Object>();
map.put("name","熊大");
map.put("age",20);
map.put("sex","男");
map.put("join_time","2010-08-17");
int i = mapper.addStudentByMap(map);
System.out.println(i);
}
如果想传入List集合类型作为参数 获取的格式为
#{list[索引]}或者$0{collection[索引]}
接口中:
/**
* 添加一个学生 要求参数类型必须是List集合
*/
int addStudentByList(List list);
映射文件中:
<!--添加学生-->
<insert id="addStudentByList" parameterType="java.util.List">
insert into student values (null,#{list[0]},#{list[1]},#{list[2]},#{list[3]})
</insert>
测试代码中:
//添加学生list集合作为参数
@Test
public void addStudentByListTest(){
List list = new ArrayList();
list.add("光头强");
list.add(30);
list.add("男");
list.add("2078-10-10");
int i = mapper.addStudentByList(list);
System.out.println(i);
}
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
1.5 ${}和#{}的区别
#{}: 表示一个占位符号,实现向PreparedStatement占位符中设置值(#{}表示一个占位符?),自动进行Java类型到JDBC类型的转换(因此#{}可以有效防止SQL注入).#{}可以接收简单类型或PO属性值,如果parameterType传输的是单个简单类型值,#{}花括号中可以是value或其它名称.
2. 动态Sql语句
Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。
2.1 动态 SQL 之<if>
我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到
<select id="findByCondition" parameterType="user" resultType="user">
select * from User where 1 = 1
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</select>
或是使用<where>标签来接收条件
<select id="findByCondition" parameterType="user" resultType="user">
select * from User
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>
2.2 动态 SQL 之<foreach>
循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)。
<select id="findByIds" parameterType="list" resultType="user">
select * from User
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
测试代码片段如下:
… … …
//获得MyBatis框架生成的UserMapper接口的实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int[] ids = new int[]{2,5};
List<User> userList = userMapper.findByIds(ids);
System.out.println(userList);
… … …
foreach标签的属性含义如下:
-
collection:代表要遍历的集合元素,注意编写时不要写#{}
-
open:代表语句的开始部分
-
close:代表结束部分
-
item:代表遍历集合的每个元素,生成的变量名
-
sperator:代表分隔符
2.3 Sql片段抽取
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
<!--抽取sql片段简化编写-->
<sql id="selectUser">
select * from User
</sql>
<select id="findById" parameterType="int" resultType="user">
<include refid="selectUser"></include>
where id=#{id}
</select>
<select id="findByIds" parameterType="list" resultType="user">
<include refid="selectUser"></include>
<where>
<foreach collection="array" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
3. Mybatis核心配置文件深入
3.1 typeHandlers标签
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
开发步骤:
①定义转换类继承类BaseTypeHandler<T> (在demo包下又创建一个hanlder包,并创建MyDateTypeHandler实体类)
②覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
③在MyBatis核心配置文件中进行注册
测试转换是否正确
public class MyDateTypeHandler extends BaseTypeHandler<Date> {
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType type) {
//实际存的Date类型,但是存在数据库中的是字符串类型的毫秒值
preparedStatement.setString(i,date.getTime()+"");
}
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
return new Date(resultSet.getLong(s));
}
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
return new Date(resultSet.getLong(i));
}
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return callableStatement.getDate(i);
}
}
在config配置文件中
<!--注册类型自定义转换器-->
<typeHandlers>
<typeHandler handler="com.demo.hanlder.MyDateTypeHandler"></typeHandler>
</typeHandlers>
测试添加学生
经过自定义转换,存进数据库的数就能迎合设计数据库表时的属性dateTime了。
3.2 主键生成策略
主键生成策略就是我们创建数据库表的时候一般会指定id为主键自增长,执行添加的时候不需要传入id的值给null即可,就可以实现自己+1。但是如果有需求,添加完毕之后立即获取id的值 这样就需要用主键生成策略
使用方法
-
在insert标签中添加属性:useGeneratedKeys = true:使用主键生成策略
-
在insert标签中添加属性 keyProperty="要封装的字段":指定将生成的id赋值给对象的那个属性
-
在添加完成后就可以获取到自增的id了
<!--添加学生-->
<insert id="addStudentByMap" parameterType="java.util.Map">
insert into student values (null,#{name},#{age},#{sex},#{join_time})
</insert>
@Test
public void addStudentTest(){
Student student = new Student();
student.setName("李老板");
student.setAge(40);
student.setSex("男");
student.setJoin_time(new Date());
System.out.println("添加之前:"+student.getId()); //null
int i = mapper.addStduent(student);
System.out.println(i);
System.out.println("添加之后:"+student.getId());
}
3.3 plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
①导入通用PageHelper的坐标
②在mybatis核心配置文件中配置PageHelper插件
③测试分页数据获取
3.3.1 导入通用PageHelper坐标
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
3.3.2 在mybatis核心配置文件中配置PageHelper插件
<!-- 注意:分页助手的插件 配置在通用mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
3.3.3 测试分页代码实现
@Test
public void test01() throws Exception{
//1.读取配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3.从工厂中获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取代理对象
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
//执行分页,一定要在查询语句之前,告知查询第2页 每一页显示3条 limit方言
PageHelper.startPage(2,3);
//5.查询所有
List<Student> list = studentMapper.findAll();
for (Student student : list) { //显示3条
System.out.println(student);
}
//创建一个分页信息对象,来查看分页封装的信息
PageInfo<Student> pageInfo = new PageInfo<Student>(list);
System.out.println("当前页:" + pageInfo.getPageNum()); // 2
System.out.println("每一页要显示的条数:" + pageInfo.getPageSize()); //3
System.out.println("总记录数:" + pageInfo.getTotal());//8
System.out.println("总页数:" + pageInfo.getPages()); //3
System.out.println("上一页:"+pageInfo.getPrePage());//1
System.out.println("下一页:"+pageInfo.getNextPage()); //3
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
sqlSession.close();
}
当然也可在映射文件查询语句中直接分页,会更简易一些。