MyBatis

MyBatis学习总结(四)

详解parameterType

Mapper.xml的每一个sql标签的parameterType属性表示输入参数,并且在sql语句中,用#{}或${}来从parameterType中取得对应的值。当输入参数的类型不同时,#{}和${}的用法有很大的区别。

基本类型

当输入参数时八个基本类型或string类型时,#{}和${}既有相似之处,又有不同的用法:

不同

第一、基本类型中#{}中的内容可以写任意值,如# {xxx};而${}中必须写value,如$ {value}(但是在新的jar包中取消了这一限制,所以这一点没有什么区别了)。

第二、对于String类型,#{}会自动给里面的参数套上单引号’ ‘,然而${}会进行原样输出,那么以下这个语句:

<select id="queryPersonByName" resultType="person" parameterType="string">
    select * from person where name= ${values} 
</select>

实际上提交给数据库的是

select * from person where name= 刘咯咯 ;

这个语句显然在sql中是不成立的,原因是数据库的字符串要加上单引号。需要改写成以下的样子:

<select id="queryPersonByName" resultType="person" parameterType="string">
    select * from person where name='${value}'
</select>

但是${}在动态排序里面就有很大的作用。一个根据输入的列名的排序语句如果用#{}写是这样的:

<select id="queryPersonOrderedByColumn" resultType="person" parameterType="string">
select * from person order by ${column } desc
</select>

将列名带入后实际上的sql语句是:

<select id="queryPersonOrderedByColumn" resultType="person" parameterType="string">
    select * from person order by 'name' desc
</select>

‘name’是一个常量,比较无结果,所以这个时候不能用#{}自动生成单引号。

第三、#{}可以防止SQL注入而${}不防止。sql注入是什么我现在也不知道,等以后要用到再说吧,参考资料

相同

都可以获取对象的值(嵌套类型对象)

第一、在模糊查询中,这两种方式虽然写法稍有不同,但是都能实现:

#{}

<select id="queryPersonByNameOrAge" resultType="person" parameterType="person">
    select * from person where name like #{name} or age=#{age}
</select>

Person person=new Person();
person.setAge(19);
person.setName("%刘%");

${}

<select id="queryPersonByNameOrAge" resultType="person" parameterType="person">
    select * from person where name like '%${name}%' or age=#{age}
</select>

Person person=new Person();
person.setAge(19);
person.setName("刘");

第二、获取对象的值的方式相同

原来直接传入的是person类的实例,现在新建一个类address,根据address的值来查询。可以直接将address作为输入参数传入:

<select id="queryPersonByAddress" resultType="person" parameterType="address">
select * from person where homeaddress=#{homeAddress} or schooladdress='${schoolAddress}'
</select>

也可以直接获取嵌套对象的值(传入person获取其address):

<select id="queryPersonByAddress" resultType="person" parameterType="person">
select * from person where homeaddress=#{address.homeAddress} or schooladdress='${address.schoolAddress}'
</select>

对象类型

当parameterType的取值是对象类型时,使用#{}和${}都必须写属性名:

<select id="queryPersonByNameOrAge" resultType="person" parameterType="person">
        select * from person where name like '%${name}%' or age=#{age}
    </select>

如果输入的值为多个,还可以用HashMap来作为parameterType的取值:

<select id="queryPersonByNameOrAgeWithHashMap" resultType="person" parameterType="HashMap">
    select * from person where name like '%${mName}%' or age=#{mAge}
</select>

传入的HashMap,key的值去匹配占位符,如果名称相同就用value去替换掉占位符:

Map<String,Object> personMap=new HashMap<>();
personMap.put("mName","刘");
personMap.put("mAge",20);
List<Person> persons=personMapper.queryPersonByNameOrAgeWithHashMap(personMap);

parameterType用HashMap来作为输入参数的一个重要用途是调用存储过程。这一点是仅仅传入一个实体类对象是无法实现的。

parameterType为HashMap时调用存储过程

存储过程无论输入参数是什么值,语法上都需要用map来传递该值.

首先在数据库中先创建两个存储过程:

1.查询某个年龄的总人数

DELIMITER //
create PROCEDURE queryCountByAgeWithProcedure (IN mAge int, OUT pNum int)
begin
select count(1) into pNum from person where age=mAge;
end
//
DELIMITER ;

2.根据id值来删除一个人的信息

DELIMITER //
Create procedure deletePersonByIdWithProcedure(In mId int)
Begin 
Delete from person where id =mId;
End
//
DELIMITER ;

mysql中创建存储过程要先将分隔符改成别的东西。参考资料

Mapper.xml

除了parameterType为HashMap之外,还需要写上一个statementType=CALLABLE以表示这个语句是执行的存储过程。之前所有的sql标签都没有写是因为statementType默认值是PREPARED;此外statementType还有一个类型是STATEMENT,表示直接操作sql,不进行预编译:

<select id="queryCountByAgeWithProcedure" statementType="CALLABLE" parameterType="HashMap">
    ...
</select>

通过关键字CALL来调取存储过程,调用的存储过程还需要写明参数的名称、类型、模式,CALL需要在大括号中调用:

<select id="queryCountByAgeWithProcedure" statementType="CALLABLE" parameterType="HashMap">
{
CALL queryCountByAgeWithProcedure(
#{mAge,jdbcType=INTEGER,mode=IN},
#{pNum,jdbcType=INTEGER,mode=OUT}
)
}
</select>

测试类方法

在测试类中,输入的参数还是和上面的一样用HashMap的put方法,同样也是要让key值和占位符匹配。不过在MyBatis中所有的存储过程的方法都没有返回值——返回值存储在HashMap中,所以接口中的方法返回值是void类型:

public interface personMapper {

    ...

    void queryCountByAgeWithProcedure(Map<String,Object> para);
    void deletePersonByIdWithProcedure(Map<String,Object>para);

}

从HashMap中取出存储过程的返回值的方式:

//用存储过程来查询年龄总人数
public static void queryCountByAgeWithProcedure() throws IOException {
    ...

    Map<String,Object> map=new HashMap<>();
    map.put("mAge",19);
    personMapper.queryCountByAgeWithProcedure(map);

    Object pNum=map.get("pNum");
    System.out.println("查询到"+pNum+"人");
    
    ...
}

存储过程的增删改也都需要在最后commit()一下:

//用存储过程来根据id删人
public static void deletePersonByIdWithProcedure() throws IOException {
Reader reader=Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession=sqlSessionFactory.openSession();

personMapper personMapper =sqlSession.getMapper(personMapper.class);

Map<String,Object> map=new HashMap<>();
map.put("mId",4);
personMapper.deletePersonByIdWithProcedure(map);
sqlSession.commit();

sqlSession.close();
reader.close();
}

jdbcType

jdbcType这个东西是MyBatis独有的,对应还有一个是javaType。虽然说java的类型能直接和数据库的类型映射,但是当java的一个变量的值是null时,数据库可能只知道其为空值,但是不知道它的具体类型是什么。

例如当parameterType为HashMap的时候,我们定义的HashMap的value类型是Object,此时如果value=null,那么数据库就无法将这个null和自己的类型进行匹配。所以Mybatis在实现可能存在未知类型的空值时要求用上jdbcType,以告诉数据库这是一个什么样的空值。

但是一般情况下数据的类型时可以确定的(例如直接写明parameterType的类型),myBatis可以直接将类型告诉给数据库,此时就不是一定要加上jdbcType了。

jdbcType和javaType的对应关系

发表评论