3.1 typeAlias
3.1.1为什么使用?
首先先来看一个问题:
在我们的查询标签中,需要返回一个数据类型,前面都是使用类的全限定名进行表示,这样写多了也会觉得很烦,很冗余,其实也就是在查询的时候需要用到这个返回类型。
3.1.2如何使用?
需要在全局配置文件(此处代指上面的mybatis-config.xml文件),进行配置。
配置方式1:
配置方式2:
配置方式3:
方式3应该避免使用。
在select中可以简单的引用一下就可以了。
注:别名是不区分大小的。
3.1.3系统自带的别名
1.基本数据类型的别名在类型前面加_
例如:int ----_int long — _long
2.包装数据类型的别名将首字母小写。
例如:Integer --integer Long-- long
3.集合类型别名也将首字母小写。
例如:List list
ArrayList arraylist
需要用到可以到他官网去查询下
3.1.4Properties
属性配置,此处需要在全局配置文件中进行配置(此处代指mybatis-config.xml)。
配置的文件上面进行申明,下面进行引用。
但是此时感觉到并没什么用,只是放在上面更加清晰一点罢了。
接下来需要讲解的是:
在mybatis-config.xml文件中进行引用,这种方式才是比较推荐的,也是实施人员最喜欢看的,还有一个url属性,表示可以从网络或者本地磁盘进行加载文件。
3.2Mapper接口和原理
3.2.1为什么使用?
我们之前的查询操作是这样的,接下来就通过这个查询代码分析下存在的问题。
1.需要使用namespace命名空间加上id去查询,从一个字符串中就可以体现出,这样很容易写错,系统也不去检查,只有在程序运行的时候才能发现。
2.传入的参数2,就是限定条件,系统也不会去检查(底层设计的就是Object对象),也只有在运行的时候才能发现。
3.代码重复,我们在获取连接对象,关闭资源,都是重复的,只有具体的增删改查操作不一致。
3.2.2如何使用?
首先定义一个接口,代码如下:
在mapper的xml文件中namespace中名称就应该用接口的全限定名。
在mapper的xml文件中的id应该对应接口中的方法名。
最后不要忘了再全局配置文件中:
配置一下
上述代码写完了,接下来进行测试:
可以看出来运行和结果也是没问题的,那么来简单分析下:
首先调用getMapper()方法传递一个EmployeeMapper的字节码对象,然后获取到EmployeMapper接口的全限定名,不就是可以得到前面的namespace的字符串吗?
然后再调用他的方法,也就是拼凑成了一开始selectOne(String string ,Object object)方法中的第一个参数,这样就很容易理解了。
底层实现:
使用了一种动态代理的设计模型,不需要掌握,知道就可以。
动态代理就不进入深入讲解了。
补充:
在上面增删改的接口方法上面可以使用返回值,Long ,Integer,Boolean包装类型和基本类型都可以,mybatis会自动返回,比如Long会返回受影响的行数,Boolean会去判断如果影响的行数>0则回返回true。
3.3resultMap
3.3.1简单使用resultMap
演示问题:
在之前我们使用的javabean封装对象的名称一定需要和数据库中的字段名称一致,若是不一致则会出现数据无法返回封装到javabean对象中,先来演示这个问题。
设计表的前面我都加了一个_。查询语句也要改一下。
<select id="selectAll" resultType="com.mybatis.dmomain.Student">
SELECT _id ,_name,_age FROM employee WHERE _id=#{id}
</select>
可以发现什么结果都没查询到,其实是查询到了,但是在封装的时候出问题了。
将表的结构和查询语句改回一样就可以查询到了。
简单使用:
resultMap中文意思就是结果映射,就是返回的结果集合和对象的映射。
此处需要在配置文件(此处代指EmployeeMappr.xml文件)。
引用结果映射:
注意:resultMap不能和resultType同时使用,并且resultMap只是用在查询上面。
在标签中,除了result还一个id的属性,他的功能跟result功能类似,但是官方介绍,如果是主键建议使用id属性,会提升一些性能。
3.3.2级联使用resultMap
此处需要新建一张表:
@Data
public class Teacher {
private Long id;
private String name;
}
假设一个学生还可以对应一个老师,或者多个老师,在查询学生信息的时候把学生对应的老师信息也查询出来。
@Data
public class Student {
private Long id;
private String name;
private Integer age;
//学生对象里面关联老师对象
private Teacher teacher;
}
接口方法:
/**
* 根据传递学生的id查询学生的全部信息
*
* @param id 学生id
* @return 返回学生对象
*/
Student getStudentAndTeacher(Long id);
XML映射文件:
方式1:使用对象.属性去封装
<!---结果集映射-->
<resultMap id="myStudent1" type="student">
<!--
column属性:对应数据表查询返回的列名
property:对应实体类中javaBean属性
-->
<id column="s_id" property="id"/>
<result column="s_name" property="name"/>
<result column="age" property="age"/>
<!--此处可以发现property属性中通过对象.属性名去关联另一个对象 -->
<result column="t_id" property="teacher.id"/>
<result column="t_name" property="teacher.name"/>
</resultMap>
<select id="getStudentAndTeacher" resultMap="myStudent1">
select s.id as s_id,s.name as s_name,s.age,s.teacher_id,t.id as t_id,t.name as t_name
from student s,teacher t where s.teacher_id=t.id and s.id=#{id};
</select>
方式2:使用association 标签
<!--
association:该标签可以封装另一个对象的属性
property:依然是对应一个当前返回类的属性
javaType:表示该属性的类全限定名
column:返回的列名
property:对象中的属性名
-->
<association property="teacher" javaType="com.mybatis.dmomain.Teacher">
<id column="t_id" property="id"/>
<result column="t_name" property="name"/>
</association>
效果也是一样的。
方式3使用association 分步查询:
方式2使用的是一个SQL语句,此处也可以使用两步去查询,比如先查询出学生的信息,然后再去根据学生中关联教师的id再去查询教师信息。
此处称为额外SQL,既然Mysql这么强大,那么就可以给我们进行完成这个操作。
做下准备工作:
前面写新建了一个teacher表和实体类,在实际中开发中既然有一个表,那么习惯的就会将该表对应发mapper接口和映射文件也给写出来。
public interface TeacherMapper {
/**
* 根据id去查询数据表中Teacher信息
*
* @param id 唯一标识
* @return 单个Teacher对象
*/
Teacher getTeacher(Long id);
}
XML映射文件:
<select id="getTeacher" resultType="com.mybatis.dmomain.Teacher">
SELECT id,name FROM teacher WHERE id=#{id}
</select>
在学生的实体类中新增加一个属性,用于封装返回的结果集
@Data
public class Student {
private Long id;
private String name;
private Integer age;
//新增属性
private Long teacher_id;
private Teacher teacher;
}
<!--
使用association进行分步查询:
1.根据学生id查询出学生的信息
2.根据查询到学生信息中关联教师的id,再去查询教师的信息
3.将查询到教师信息设置到学生对象中
-->
<resultMap id="myStudent2" type="student">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<result column="teacher_id" property="teacher_id"/>
<!--property:实体类中对象
select:发送查询语句
column:将返回列的值传递过去
-->
<association property="teacher" select="com.mybatis.mapper.TeacherMapper.getTeacher"
column="teacher_id"/>
</resultMap>
<select id="getStudentByIdStep" resultMap="myStudent2">
SELECT id,name,age,teacher_id FROM student WHERE id=#{id}
</select>
select id="getTeacher" resultType="com.mybatis.dmomain.Teacher">
SELECT id,name FROM teacher WHERE id=#{id}
</select>
分步查询和延迟加载:
前面在使用方式3发送额外SQL查询的时候,不管每次是否需要查询教师的信息,Mybatis每次都会去关联查询教师的信息,这样当我们不需要的时候则会浪费性能。
首先重景在现:
@Test
void testStudent() {
SqlSession session = MyBatisUtil.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student student = mapper.getStudentByIdStep(2L);
//此处我只想获取员工的年龄信息
System.out.println(student.getAge());
session.close();
}
可以发现我并没有调用Teacher的get方法,并没有想去获取该属性,他就将我给查询出来了。
只需要在全局配置文件中
<settings>
开启懒加载模式
<setting name="lazyLoadingEnabled" value="true"/>
不积极的去查找关联属性
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
3.3.3collection标签
该标签看名字就知道主要是来封装集合操作的。
表内容增加下:
接口方法
/**
* 根据教师的id查询该教师关联的所有学生信息
*
* @param id 教师id
* @return 学生信息
*/
Teacher getTeacherPlus(Long id);
<resultMap id="myTeacher" type="com.mybatis.dmomain.Teacher">
<!--封装查询到的教师信息-->
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--封装查询到学生的信息
collection :集合结果集
ofType: 集合中对象类型
-->
<collection property="students" ofType="student">
<id column="s_id" property="id"/>
<result column="s_name" property="name"/>
<result column="age" property="age"/>
<result column="teacher_id" property="teacher_id"/>
</collection>
</resultMap>
<select id="getTeacherPlus" resultMap="myTeacher">
select s.id as s_id,s.name as s_name,s.age,s.teacher_id,t.id,t.name
from student s,teacher t
where s.teacher_id=t.id and t.id=#{id};
</select>
@Data
public class Teacher {
private Long id;
private String name;
//一个老师中可以对应多个学生对象
private List<Student> students = new ArrayList<>();
}
测试方法
@Test
void testTeacher() {
SqlSession session = MyBatisUtil.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teachers = mapper.getTeacherPlus(2L);
List<Student> students = teachers.getStudents();
for (Student s : students) {
System.out.println(s);
}
session.close();
}
分步查询:
鉴于上面的分步查询,此时也来尝试下:
接口方法:
/**
* 查询该教师所关联的所有学生
*
* @param id 教师id
* @return 教师中包含的学生
*/
Teacher getTeacherInStudentByStep(Long id);
XML映射文件:
<!---结果集映射-->
<resultMap id="teacherIndStudent" type="com.mybatis.dmomain.Teacher">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="students"
select="com.mybatis.mapper.StudentMapper.getStudentByTeacherID" column="id"/>
</resultMap>
<select id="getTeacherInStudentByStep" resultMap="teacherIndStudent">
SELECT id,name FROM teacher WHERE id=#{id}
</select>
<select id="getStudentByTeacherID" resultType="com.mybatis.dmomain.Student">
SELECT id,name,age,teacher_id FROM student WHERE teacher_id=#{id}
</select>
测试方法:
@Test
void testTeacher() {
SqlSession session = MyBatisUtil.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacherInStudentByStep(1L);
System.out.println(teacher.getName());
// List<Student> students = teacher.getStudents();
// for (Student s : students) {
// System.out.println(s);
// }
session.close();
}
可以看到他也只是执行了一条SQL,也是按需加载。
此处补充一个知识点:
在上面我们使用了分步查询,都有一个column属性,但是传递的都是一个值,此处的需求就是传递多个值过去.
select="com.mybatis.mapper.StudentMapper.getStudentByTeacherID" column="id"/>
</resultMap>
column="{key1=column1,key2=column2}"
这样传递即可,演示下:
即使取错了key,不会报错,就是获取不到值。
可以单独的开启是否启动懒加载模式:
3.4参数处理
3.4.1为什么使用?
首先来看一个查询操作:
此处我们做查询,只能传递一个参数,那么我们需要传递多个参数呢?
此处先说一下:
1.针对传递单个基本的数据类型
<select id="selectone" resultMap="baseResultType">
SELECT _id,_name,_age FROM student WHERE _id=#{xxx}
</select>
#{}里面的属性名可以任意写都可以映射,任意写也是符合标识符命名规范的。
2.传递javaBean对象
<select id="selectone" resultMap="baseResultType">
UPDATE student set id=#{id},name=#{name},age=#{age}
WHERE id=#{id}
</select>
此时传递是对象,会去取getter的属性值,若是没有则会报错。
3.传递Map对象
此处会根据key的值去查询value的值,需要在取值的时候用传递的key。
4.传递@Para(“key”)注解
此时取值则要根据注解中的key进行取值。
3.4.2使用POJO封装
先做一些准备工作,下面处理方式也会用。
1.新建一个实体类:
EmployeeLogin,封装getter和setter方法以及提供有参和无参构造器。
2.接口中的方法更改下:
EmployeeLogin selectOne(EmployeeLogin employeeLogin);
此时他需要接收一个对象,此处我们就来模拟一个登陆操作。
3.将数据表中添加两个字段并且给些数据。
4.修改Mapper.xm中的SQL查询语句。
<select id="selectOne" resultType="com.mybatis.entry.EmployeeLogin">
SELECT * FROM employe WHERE username=#{username} AND
password=#{password}
</select>
准备工作完成了,接下来就是调用。
可以发现这样也是可以的,但是如果有很多个需要传递参数就得创建很多个对象,这样下去也比较麻烦。
3.4.3使用Map进行封装
3.4.4使用注解@Param("")
可以发现也是可以的,这个也是实际开发中最常用的一种方式,需要重点去掌握。
传递集合的时候肯定是需要 用到这个注解的。
注意:注解中传递的字符串需要和执行#{}括号中的字符串一样。
3.4.5#和$区别
这两个符号在前面都有使用过,此处就来讲解下他们的区别。
通过运行日志可以看出:
使用#:
SELECT id,username,password FROM userinfo WHERE
username=? AND password=? Parameter:root(String) , 12356(String)
使用$:
SELECT id,username,password FROM userinfo WHERE
username=root AND password=123456
相同点:
他们都可以从对象中取出数据。
不同点:
1.使用#传递的参数会先转换为占位符,再通过设置占位符参数的方式进行设置值。
2.使
传
递
的
参
数
,
会
直
接
把
解
析
出
来
的
参
数
作
为
S
Q
L
语
句
的
一
部
分
。
如
何
选
择
的
问
题
:
当
我
们
需
要
传
递
的
参
数
作
为
S
Q
L
语
句
一
部
分
的
时
候
需
要
使
用
传递的参数,会直接把解析出来的参数作为SQL语句的一部分。 如何选择的问题: 当我们需要传递的参数作为SQL语句一部分的时候需要使用
传递的参数,会直接把解析出来的参数作为SQL语句的一部分。如何选择的问题:当我们需要传递的参数作为SQL语句一部分的时候需要使用符号,当仅仅要设置参数的时候使用#即可。
下面演示下:根据id进行升序排序。
接口方法:
void select(@Para(“condition”) String condition);
xml文件:
SELECT * FROM student ORDER BY id ${condition};
调用:
select(“ASC”)
执行结果:
SELECT * FROM student ORDER BY id ASC;
<select id="selectAll" resultType="com.mybatis.dmomain.Student">
SELECT _id FROM employee WHERE id=#{id,jdbcType=NULL}
</select>
对于传递null的时候,oracle数据库不支持,无法进行映射,需要改一下,但是一般不去传递null就可以了。
3.4.6Mybatis内置参数
_parameter:
当传递单个数据的时候,_parameter就代指这个参数
当传递多个数据的时候,_parameter代指的就是这个参数的key
_databaseId:
代表当前数据库的配置的值。
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
3.5 Mybatis的插件
开发工具使用的是idea,安装下插件方便后续更好的开发。
写代码会有一些实质性的提示,比如你在接口中定义了一个方法,在mapper文件中没有定义该方法他会直接报错,你点击可以直接给你生成。
3.5.1Log4j日志打印
log4j.properties:
放在resources资源目录下
#所有日志
log4j.rootLogger = DEBUG,stdout,file,WARN,DAILY_ALL
log4j.logger.org.apache.ibatis=warn
log4j.logger.java.sql=warn
log4j.logger.org.springframework=warn
# Druid
log4j.logger.druid.sql=DEBUG
log4j.logger.druid.sql.DataSource=warn
log4j.logger.druid.sql.Connection=warn
log4j.logger.druid.sql.Statement=DEBUG
log4j.logger.druid.sql.ResultSet=warn
#设置包名的输出级别
log4j.logger.cn.wdq=DEBUG
#控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.Threshold=DEBUG
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss}[ %p ]%m%n
#所有文件输出
log4j.appender.DAILY_ALL=org.apache.log4j.DailyRollingFileAppender
log4j.appender.Threshold=WARN
log4j.appender.DAILY_ALL.layout=org.apache.log4j.PatternLayout
log4j.appender.DAILY_ALL.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm} %-50.50c(%L) - %m%n%n
log4j.appender.DAILY_ALL.File=D:/dispatch.log
3.5.2LomBok插件
安装完重启下IDEA就可以用了。