Mybatis2
本章目标:
-
myBatis类型别名处理
-
myBatis参数处理
-
myBatis结果集类型
-
myBatis结果集列名和属性名称对应方式处理
-
附录
本章内容
一、类型别名(typeAliases)处理
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如,在配置文件中加入以下配置:
自己配置类型别名
配置方式一:
<typeAliases> <typeAlias type="com.entity.Dept" alias="dept"/> </typeAliases>
配置方式二:直接配置包名:底层会为包中的每一个类自动起别名(类的短名(movie|Movie))
<typeAliases> <package name="com.entity"/> </typeAliases>
已有的类型别名
别名 类型
_byte | byte |
---|---|
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
二、MyBatis参数处理:
-
单个参数,参数非实体类类型(简单数据类型—八种基本数据类型及对应的封装类、Date,String)
接口方法
public void deleteStudentById(Integer studentId);
映射文件
<delete id="deleteStudentById" parameterType="integer"> delete from student where student_id=#{student_id} </delete> <delete id="deleteStudentById" parameterType="int"> delete from student where student_id=#{student_id} </delete>
单个参数:parameterType可以不写,管理比较松散,如果写了,只要类型匹配就可以了(映射期接口中方法的参数类型和映射文件中的parameterType的属性值应该匹配,parameterType和参数类型一致或者能够方法参数类型的父类类型),名称无所谓,如果参数为int,配置为short,则会出现异常,参数名称没有要求
### Error updating database. Cause: org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property='student_id', mode=IN, javaType=class java.lang.Short, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}. Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter #1 with JdbcType null . Try setting a different JdbcType for this parameter or a different configuration property. Cause: java.lang.ClassCastException:
-
多个参数,且非实体类类型
接口中的方法
public void saveStudent1(String studentName,String studentSex);
映射文件第一种方式
<insert id="saveStudent1" > insert into student(student_id,student_name,student_sex)values(20,#{arg0},#{arg1}) </insert>
映射文件第二种方式
<insert id="saveStudent1" > insert into student(student_id,student_name,student_sex) values(21,#{param1},#{param2}) </insert>
以上两种方式的问题:参数名称补明确,不能望文生义,于是有第三种方式,借助于Param注解
映射文件第三种方式(重点掌握)
接口方法:
public void saveStudent1(@Param("sname")String studentName,@Param("ssex")String studentSex);
映射文件:
<insert id="saveStudent1" > insert into student(student_id,student_name,student_sex)values(22,#{sname},#{ssex}) </insert>
-
实体类类型参数
接口方法
public void saveStudent(Student s);
映射文件
<insert id="saveStudent" parameterType="Student"> insert into student(student_id,student_name,student_sex,student_grade,student_profession,student_age) values(#{student_id},#{student_name},#{student_sex},#{student_grade},#{student_profession},#{student_age})</insert>
注意:#{实体类对象属性名称} 参数名 称要和pojo属性名称一致,原理:先找getter,再根据属性进行匹配
-
带多个参数并且有的为实体类类型
映射器接口:
public List<Student> selectStudent2(@Param("s")Student s,@Param("sname")String sname);
映射文件 :
<select id="selectStudent2" resultType="student"> select * from student where student_id=#{s.student_id} and student_name=#{sname} </select>
-
map参数
接口
public List selectEmpByEnameAndEmpno1(Map params);
映射文件:依靠map集合的键进行值的引用
<select id="selectEmpByEnameAndEmpno1" parameterType="map" resultType="emp"> SELECT * FROM emp WHERE ename LIKE CONCAT('%',#{key1},'%') AND empno=#{key2} </select>
测试
public void test5(){ SqlSession sqlSession= MyBatisUtil.crateSqlSession(); EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class); Map<String,Object> param=new HashMap<>(); param.put("key1","A"); param.put("key2",(short)7900); List<Emp> emps=empMapper.selectEmpByEnameAndEmpno1(param); logger.info(emps); MyBatisUtil.closeSqlSession(sqlSession); }
-
精确指定参数类型,解决类型的对应问题
#{property,javaType=int,jdbcType=NUMERIC}
不指定会按照默认的处理方式进行处理,但是一些特殊的数据类型如Date类型可能不符合我们的要求,那就需要精确指定(uitl.Date能表示 年月日 年月日时分秒,如果不指定,默认会取年月日时分秒)
#{property,javaType=int,jdbcType=NUMERIC}<insert id="saveMovie" parameterType="Movie"> <!-- #{实体类对象的属性名称}:自动获取对应的属性值,默认调用#号后的属性对应的getter获取属性值,如果getter找不到,会直接找同名的属性 --> insert into movies(movie_code,movie_name,movie_time,movie_date,movie_price,movie_com,movie_type)values(${movie_code},#{movie_name,javaType=string,jdbcType=VARCHAR},#{movie_time,javaType=int,jdbcType=INTEGER},#{movie_date,javaType=date,jdbcType=DATE},#{movie_price,javaType=float,jdbcType=FLOAT},#{movie_com,javaType=int,jdbcType=INTEGER},#{movie_type,javaType=int,jdbcType=INTEGER}) </insert>
-
$和#的区别(面试爱问)
$:引用的值会直接替换sql语句中的部分,编译的时候值已经替换了,常见于设置表名列名等信息
#
:会在sql中先处理为?,进行预编译,编译之后,然后再进行值的替换 -
总结
如果参数多于4个以上,建议不要单独写,这种情况在特殊业务情况下必须使用实体对象和map比较通用
三、myBatis结果集结果集返回类型的处理
-
返回单个对象(单个实体类对象)
接口方法
public Student getStudentById(Integer studentId);
映射文件
<select id="getStudentById" parameterType="integer" resultType="Student"> select * from student where student_id=#{ids} </select>
-
返回集合(封装的依然为实体类对象)
接口方法
public List<Student>getAll();
映射文件集合中封装的实体类类型
<select id="getAll" resultType="Student"> select * from student </select>
-
返回Map,返回的是一行记录,将一行记录封装到map中 ,键为结果集的列名,值就是结果集对应列的值
实例1:(建议用实体类对象返回,了解)
接口方法
public Map<String, Object> getStudentReturnMap(Integer studentId);
映射文件
<select id="getStudentReturnMap" resultType="map"> select * from student where student_id=#{ids} </select>
测试方法:
session=sqlSessionFactory.openSession(); StudentMapper mapper=session.getMapper(StudentMapper.class); Map<String,Object> map=mapper.getStudentReturnMap(id); System.out.println(map); session.commit(); session.close();
实例二:返回多个聚合函数的结果(多见)
接口方法
public Map<String, Object> getCountAndAge();
映射文件
map中封装的数据:
结果集的列名-(键)———对应列的值(值)
<select id="getCountAndAge" resultType="map"> select count(student_id) as studentCount,max(student_age) as studentAge from student </select>
测试
SqlSession session=sqlSessionFactory.openSession(); StudentMapper mapper=session.getMapper(StudentMapper.class); Map<String,Object> map=mapper.getCountAndAge(); System.out.println(map); session.commit(); session.close();
-
返回多条记录List
返回分组聚合的结果(多见,重点掌握)
接口方法
接口方法: /*接收分组统计之后的结果 * * {studentCount=10, studentAge=25} {studentCount=25, student_grade=1, studentAge=26} {studentCount=2, student_grade=2, studentAge=25} {studentCount=2, student_grade=3, studentAge=25} {studentCount=1, student_grade=4, studentAge=27} 会将每一行数据先封装为一个map,以结果集的列名作为键,对应列的值作为值, 然后将map封装到List中 * * */ public List<Map<String, Object>> getCountAndAgeByGrade();
映射文件
<select id="getCountAndAgeByGrade" resultType="map"> select student_grade,count(student_id) as studentCount,max(student_age) as studentAge from student group by student_grade </select>
测试
SqlSession sqlSession=DBTools.getSqlSession(); //会根据命名空间+id去找对应的标记,执行标记中sql语句,根据配置的结果集类型进行封装 //根据sqlSession。getMapper传入的接口类型,得到一个接口对应的实现类型对象 //调用接口中的方法(会自动执行映射文件中对应的sql) StudentMapper mapper=sqlSession.getMapper(StudentMapper.class); //获得一条记录,返回值为map List<Map<String,Object>> l=mapper.getCountAndAgeByGrade(); for(Map<String, Object> map:l){ System.out.println(map); }
四、结果集的列名和实体类属性名称对应方式的处理
默认按照属性名称和结果集列名一致的原则进行封装,如果不一致,会有以下处理方式
-
如上虽然数据库已经查到了数据,但是由于数据库字段的名称和实体对象的属性名称不一致,这样resultType属性就不能实现自动的设置,遇到这样问题,有如下三种解决方案
-
使用AS关键字来解决
<select id="findAllPerson"resultType="person"> SELECT person_id AS personId,person_name AS personName,person_age personAge FROM person </select>
-
如果你的项目中采用驼峰命名法,那么可以在mybatis-cofig.xml文件中进行设置person_id==>personId(要求:项目中的pojo需要符合驼峰命名法的规则)
结果的列名为下划线风格,实体类的属性名称为驼峰风格,才有必要这样使用
<settings> <setting name="mapUnderscoreToCamelCase" value="true"></setting> </settings>
注意:如上设置一定实体类的属性要符合驼峰命名法才行,否则不能起到效果
-
采用resultMap方式来解决(推荐使用)
可以解决如下问题:
1)列名和属性名称对应的问题
2)数据类型对应的问题(如何对应,查看笔记第四部分附录)
3)关联映射
配置resultMap(自定义结果集的映射方式)
<mapper namespace="com.dao.GradeMapper"> <resultMap type="grade" id="gradeResult"> <id property="gradeId" column="grade_id" javaType="integer" jdbcType="INTEGER"/> <result property="gradeName" column="grade_name" javaType="string" jdbcType="VARCHAR"/></resultMap> <select id="getAll" resultMap="gradeResult"> select * from grade </select> </mapper>
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
注意:select标记的resultMap属性和resultType属性的区别
resultType:属性值是一个具体的类型,用的是列名和属性名称一致原则自动封装
resultMap:属性值是映射文件中resultMap标记的id属性的属性值,想采用自定义的映射规则去进行封装
五、附录
数据库数据类型和JAVA类型的对应关系
两个元素都有一些属性:
属性 | 描述 |
---|---|
property | 映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
column | 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType | 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
支持的 JDBC 类型
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。
BIT | FLOAT | CHAR | TIMESTAMP | OTHER | UNDEFINED |
---|---|---|---|---|---|
TINYINT | REAL | VARCHAR | BINARY | BLOB | NVARCHAR |
SMALLINT | DOUBLE | LONGVARCHAR | VARBINARY | CLOB | NCHAR |
INTEGER | NUMERIC | DATE | LONGVARBINARY | BOOLEAN | NCLOB |
BIGINT | DECIMAL | TIME | NULL | CURSOR | ARRAY |