MyBatis复习笔记 2021-9-15
MyBatis复习笔记
什么是MyBatis:
- 是一款优秀的持久层框架
- 避开了几乎所有的JDBC代码和手动设置参数以及获取结果集的过程
- 可以使用简单的 xml 或者注解来配置映射的原生信息,将接口和Java实体类映射成数据库中的记录
MyBatis的官方中文文档地址:http://www.mybatis.org/mybatis-3/zh/index.html
什么是持久层框架
- 对于瞬时来说,持久层是可以吧数据保存在数据库或者硬盘一类可以保存很长时间的设备中,不向放在内存中那样,断电就会消失,也就是把数据存在持久化的设备上
- JDBC就是一种持久化机制,文件io也是一种持久化机制
为什么需要持久化服务,以及为什么需要持久化框架
- 企业应用中的数据很重要(各种订单数据、客户数据、库存数据之类的),比应用程序本身更重要, 所以需要把数据持久化。持久化可以通过很多方式,写文件和数据库都可以。只是现在企业一般都会选择把数据持久化到数据库中,因为可以很方便的查询统计分析,但数据库的数据最终还是会写到磁盘上的。Java 程序员为了操作数据库, 最开始是使用JDBC来进行的,但是这种方式的开发效率低 ,要写一堆重复代码,加上关系数据库和对象本身存在所谓的阻抗不匹配情况,所以 为了提高开发效率,有人发明了 ORM 即 对象关系映射框架
什么是持久层
- 完成持久化工作的代码块---->dao层
- 在系统的架构中需要有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现,所以有了持久层的概念
为什么需要MyBatis
- MyBatis就是帮助我们开发将数据存入数据库中,从数据库取数据
- 可以简化重复的代码,提高开发的效率
MyBatis的第一个程序逻辑顺序
- 搭建环境:就是创建表以及插入相应的数据
- 导入相关的MyBatis的jar包
- 编写MyBatis的核心配置文件:可以去查看帮助文档在入门中(http://www.mybatis.org/mybatis-3/zh/index.html)
- 编写MyBatis工具类(可以不编写)
- 创建实体类
- 编写Mapper类
- 编写Mapper.xml配置文件(要与Mapper类同名)
1.配置文件中的namespace十分重要因为他的作用是绑定Dao接口,即面向接口编程,绑定了接口之后,就不需要写接口的实现类,mybatis会通过该绑定自动的找到相对应执行的语句
<?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.sli.dao.UserMapper">
<select id="selectUser" resultType="com.sli.pojo.User">
select * from user
</select>
</mapper>
- 编写测试类测试
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selectUser();
MyBatis的增删改查操作
注意点:
- 增.删.改需要提交事务,此处可以显示的声明在SqlSessionFactory对象的openSession方法中设置参数为true就可以,在第四部编写的工具类中,否则则需要手动提交
session.commit();
- 接口所有的普通参数,尽量都在前面加上@Param参数,尤其是多个参数时,必须写上,类似于这样:
List<User> queryUsersById(@Param("id") int id);
- 有时根据业务需求,可以考虑使用map传递参数
- sql的配置文件中尽量将Parameter 和 resultType都写
- 模糊查询的like语句如何编写
- 在java中编写sql通配符如下1
- 在sql语句中拼接通配符,会引起sql注入问题如下2
第一种:
string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);
/*
<select id=”selectlike”>
select * from foo where bar like #{value}
</select>
*/
第二种:
string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like "%"#{value}"%"
</select>
mybatis-config.xml核心配置文件
configuration(配置)--->其中包含下方所有的,也就是下方所有的都得写在
<configuration></configuration>
中
properties(属性)--->常用于规定database.properties(driver url username password)的位置
<properties resource="database.properties">
settings(设置)主要使用懒加载,日志实现,缓存开启关闭
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
typeAliases(类型别名)只和xml的配置有关,减少完全限定名的冗余
<typeAliases>
<typeAlias type="com.sli.pojo.User" alias="User"/>
</typeAliases>
当这样配置时User可以在任何使用com.sli.pojo.User的地方
同时也可以指定一个包名,MyBatis会在包下面搜索需要的JavaBean
<typeAliases>
<package name="com.sli.pojo"/>
</typeAliases>
每一个在包com.sli.pojo中的Java Bean 在没有注解的情况下,会使用Bean的首字母小写的非限定类名来作为他的别名
若有注解,则别名为其注解的值,如下:
@Alias("user")
public class User {
...
}
environments(环境配置)--->其中包含environment环境变量
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
- 配置MyBatis的多套运行环境,将sql映射到多个不同的数据库上面,必须指定一个为其默认的运行环境(通过environments 的 default属性指定)
mapper元素(能找到指定的接口,而接口又有xml文件的namespace属性来绑定这个接口)映射器:定义映射的sql语句文件:推荐使用
<!--
使用映射器接口实现类的完全限定类名
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
或者
<!--
将包内的映射器接口实现全部注册为映射器
但是需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
ResultMap
查询为null问题解决方式推荐
select * from user where id = #{id}
<可以看做>
select id,name,pwd from user where id = #{id}
原因分析:mybatis会根据这些查询的列名(会将列名转化为小写,数据库部分大小写),去对应的实体类中查找相应的列名即set方法设置,找不到对应的setpwd(),所以将password返回为null,以上都是基于自动映射
推荐解决方案:使用结果集映射--->ResultMap
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
分页
多对一,一对多处理
自己理解:
- 多个学生对应一个老师
- 反则就是对于学生,就是一个多对一的现象
- 创建两个表,中间需要绑定一个语法(alter table 从表名 add 【constraint 外键名】 foreign key(从表外键的字段) references 主表名(主表主键))
案例1 多对一处理
- 创建表
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, 'f老师');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '1f', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '2f', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '3f', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '4f', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '5f', '1');
- 在代码中添加注解
//GET,SET,ToString,有参,无参构造
public class Teacher {
private int id;
private String name;
}
//GET,SET,ToString,有参,无参构造
public class Student {
private int id;
private String name;
//多个学生可以是同一个老师,即多对一
private Teacher teacher;
}
- 编写实体类对应的Mapper接口2个
public interface StudentMapper {
}
public interface TeacherMapper {
}
- 编写Mapper接口对应的mapper.xml文件2个
<?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.sli.mapper.StudentMapper">
</mapper>
<?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.sli.mapper.TeacherMapper">
</mapper>
- 给StudentMapper接口增加方法
//获取所有学生及对应老师的信息
public List<Student> getStudents2();
- 编写对应的Mapper文件
<?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.ali.mapper.StudentMapper">
<!--
按查询结果嵌套处理
思路:
1. 直接查询出结果,进行结果集的映射
-->
<select id="getStudents2" resultMap="StudentTeacher2" >
select s.id sid, s.name sname , t.name tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!--关联对象property 关联对象在Student实体类中的属性-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
</mapper>
- 去mybatis-config.xml中注册Mapper
<mappers>
<mapper class="com.sli.dao.TeacherMapper"/>
<mapper class="com.sli.dao.StudentMapper"/>
</mappers>
- 测试
@Test
public void testGetStudents2(){
SqlSession session = MybatisUtils.getSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> students = mapper.getStudents2();
for (Student student : students){
System.out.println(
"学生名:"+ student.getName()
+"\t老师:"+student.getTeacher().getName());
}
}
案例二 一对多处理
- 编写实体类
public class Student {
private int id;
private String name;
private int tid;
}
public class Teacher {
private int id;
private String name;
//一个老师多个学生
private List<Student> students;
}
- 与之前的一样,搭建测试环境
- 编写接口与对应的Mapper配置文件
<mapper namespace="com.kuang.mapper.TeacherMapper">
<!--
思路:
1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
2. 对查询出来的操作做结果集映射
1. 集合的话,使用collection!
JavaType和ofType都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型
ofType指定的是映射到list集合属性中pojo的类型。
-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname , t.name tname, t.id tid
from student s,teacher t
where s.tid = t.id and t.id=#{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>
</mapper>
- 将Mapper注册到mybatis-config中
<mappers>
<mapper resource="mapper/TeacherMapper.xml"/>
</mappers>
- 测试
public void testGetTeacher(){
SqlSession session = MybatisUtils.getSession();
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher.getName());
System.out.println(teacher.getStudents());
}