1、简介
- 持久化就是程序的数据在持久态和瞬时状态的转化的过程
- 内存:断电即失
- mybatis是持久层的框架,层是界限十分明显
2、 Mybatis的使用步骤
- 首先需要连接数据库,导入Mybatis和连接数据库的依赖
- 要进行操作数据库,需要获取到对应数据库的SQLSession,进行数据库的操作
- 构建工具类,来获取SQLSession对象 SQLSessionFactoryBuilder->SQLSessionFactory->Sqlssssion
- 问题:根据什么来创建指定数据库SQLSessionFactory
- 需要加载关于Mybatis-config的配置文件,通过它来创建SQLSessionFactory
//对应的代码
String resource="mybatis-config.xml" //配置的对应数据源的信息
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessonFactory = new SqlSeesionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
- 接着在Dao层中编写一个类为UserDao,里面有关于pojo操作的方法,返回的是pojo的变量
- 映射对应数据中的关系,类似于之前的实现类
- 定义UserDaoImpl.xml(UserMapper) 通过nameSpace,进行和UserDao进行绑定
- select标签进行绑定对应的方法和返回值 ** id=方法名** resultType=返回值的类型(返回值的类型也是对应的类的权限定名称,要不然我不晓得是谁)
- 值得注意的一点是:对应的xml文件在maven工程中不能很好的识别,需要添加对应的依赖build,来识别xml文件的配置。还需要向Mybatis核心配置文件中进行注册,
对应的标签为<mapper>,resource="com/kuang/dao/userMapper.xml" - main方法中,通过工具类MyBatisUtils进行获取SqlSession,进行执行数据库的操作
SqlSession sqlSession = MybatisUtils.getSqlsession();
******重要的一步,sqlSession加载的是接口的类
//如何执行对应的sql语句 通过反射这里进行获得,而反射只能反射对应的接口
UserDao userDao = sqlSession.getMapper(UserDao.class);
//获取到之后就能执行对应的方法,通过方法进行调用带mapper文件中的配置
userDao.getUserList(); //就可以映射到mapper文件去执行对应的方法
sqlsession.close();
总结:核心的三个类: SqlSessionFactoryBuilder、 SqlsessionFactory(默认是静态单例模式:只能被一次实例化)、 SqlSession
3、增删改查
回顾
- namespace在mapper.xml文件中的,必须和接口的权限定名字相同
- select id就是对应接口中的方法名 resultType是SQL语句的返回值,也是接口中方法的返回值 parameyerType对应的是方法中参数的类型
- select * from User where id = #{id} 这个id代表着传过来的id
- 接口中的方法
List<User> findUserById(int id);
添加一个user
<insert id="addUser" parameterType="user的全限定名称">
//mybatis是数据库的名字,user是对应的表 (连上id才可以这样去写) //后面的占位符对应的是user的属性,必须要11对应
insert into mybatis.user(id,name,pwd) values (#{id},#{name},#{pwd});
</insert>
//main方法中添加User
UserDao userdao = Sqlsession.getMapper(UserDao.calss);
userdao.addUser(new User(10,“xiaohong”,123456)); //添加的一个类的具体的实例,引用的传递,每次传的是一个新的地址
增删改查之后必须要提交事务,通过sqlsession.commit();- 添加一个User时,编写Sql具有多个属性,需要将User类型转为Map类型,可以传递自定义的类型 int addUser(User user); int addUser(
Map<String,Object>map); //定义为object就可以为任意类型了 - 因为map普通的键可以实现一一映射,且查询的效率为O(1) ` 这样我们的id的名字就不需要和属性一一对应,且属性太多的时候,可以只插入其中的一部分 对应的insert为
insert into mybatis.user (id,name,pwd) values (#{UserId},#{UserName},#{Userpwd}) `
4、模糊查询
模糊查询最容易导致的问题-SQL注入
1、java代码执行的时候,传递通配符% %
//进行拼接字符串
List<User> userList = mapper.getUserLike("%曹%");
2、在SQL拼接中使用通配符
select * from mybatis.user where name like "%"#{value}"%"
5、配置解析
//配置多个环境
<configuration>
<environments default="development"> //默认的环境
<environment id="development">
<transactionManager type="JDBC"/> //事务的管理器
<!--数据源--> //加个池子,让并发web应用更加的流畅
<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>
<environment id="2">
<transactionManager type="JDBC"/> //事务的管理器
<!--数据源--> //加个池子,让并发web应用更加的流畅
<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>
//每一个mapper文件都需要在mybatis.xml核心配置文件中进行注册
<mappers>
<mapper resource="com/ahu/dao/UserMapper.xml"></mapper>
</mappers>
</configuration>
问题1:如何传递数据库连接的信息,定义一个properties文件,通过标签进行读取
//注意
<properties resource="db.properties"> //注意文件名以properties结尾
//或者直接在proiperties中配置连接的信息
//注意这里的properties标签放在最上面
<properties>
<property name="username" value="root"/>
<property name="ped" value="123456"/>
</properties>
//如果在配置文件中配置,又在properties标签中配置,优先级是配置文件中的高
问题2、类型别名
给java类型设置一个短的名字,减少使用权限定名字的冗余 在配置文件中使用标签<typeAliases>进行配置,位置放在properties后面
//第一种
<typeAliases> //类型别名
<typeAlias type="com.ahu.pojo.User" alias="User"/>
</typeAliases>
//第二种
<typeAliases> //类型别名
<package name = "com.ahu.dao.User">
</typeAliases>
- 第一种不太适合类比较多的情况
- 第二种适合自定义实体类比较多的情况,默认为类的首字母小写
- 也可以使用注解在每个实体类的开头@Aliase("自定义别名")
问题3、映射
//第一种:使用resource来进行映射
<mappers> //这是路径名字
<mapper resource="com/ahu/dao/UserMapper.xml"></mapper>
</mappers>
//第二种:使用calss进行映射
<mappers>
<mapper calss="com.ahu.dao.UserDaoMapper.class"></mapper>
</mappers>
//第三种:使用package进行映射
<mappers>
<mapper package="com.ahu.dao"></mapper>
</mappers>
注意:第一种不需要接口和.xml 文件文件名字相同,而剩下两种需要名字相同
6、结果集映射
<resultMap id="userMap" type="User">
//column指的是数据库中的字段,property实体类的属性 解决实体类的属性名字和数据库中的字段的映射
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
//id 指的是接口中的方法名字
<select id="getUserId" resultMap="userMap">
select * from mybatis.user where id = #{id};
</select>
问题: 一对多和多对一的关系
7、日志工厂
//默认日志的配置,注意这个setting放的位置 **stdout标准输出**
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
//使用其他的日志类型,只需要进行改一下value配置 LOG4J,但不是默认的,需要导入配置文件
定义一个log4J.properties的文件
简单使用
//在一个类中进行获取信息
static Logger logger=Logger.getLogger(UserDaoTest.class);
public void testLog4j(){
logger.info();
logger.debug();
logger.error();
}
8、分页
目的:减少数据的处理量
使用limit实现分页
//语法
select * from user limit startIndex,pagesize
select * from user limit 3; #[0,n]
使用mybatis实现页面的分页,核心SQL
- 接口
- Mapper.xml
- 测试
9、使用注解开发
面向接口的编程,省略编写XML文件
- 1、注解在接口上实现
@Select("select * from user")
List<User> getUsers();
- 2、需要在核心配置文件中绑定接口
<mappers>
<mapper class="com.ahu.dao.UserMapper" />
</mappers>
- 3、 底层实现 动态代理 + 反射
- 4、使用注解进行增删改查 (使用注解指定的进行查询)
//方法中具有多个参数,在前面必须加上那个注解@param
@Select("select * from user where id = #{id}")
User getUserByID(@Param("id") int id)
@Insert(“insert into user(id,name,pwd) values (#{id},#{name},#{password})”)
int addUser(User user);
@Update("update user set name = #{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id = #{id}")
int deleteUser(int id);
//注意,参数前不加注解就这样的
@delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
关于@param的注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
- 我们在SQL文件中引用的就是我们这里的@Param("uid")中设定的属性名
补充#{}和${}的区别
- #{}可以防止SQL的注入,一般只用#{}
- 类似于prepareStatement和pareparement的区别
10、Mybatis详细的执行流程
- 首先,创建MyBatis.xml文件,配置连接的信息 解析xml文件
- 接着,需要连接数据库,采用的是SqlSession,则需要得到他
- 通过SqlSeesionFactoryBuilder加载Mybatis核心配置文件,通过流的方式进行读取解析
- 然后,得到SqlSessionFactory,通过它的方法openSession(),来得到Sqlsession
- opensession()方法中设置为true的时候,则自动进行提交事务
- main方法中,通过工具类获取到SqlSession,通过SqlSeesion的方法getMapper()方法,通过的反射的原理类似于加载接口,获取到接口,可以来执行接口中对应的方法
- 增删改的时候更需要进行提交事务 sqlSession.commit();
- 执行的时候有一个事务管理器,创建executor执行器,创建SqlSession,来实现CRUD,当执行失败的时候进行回滚
注意:新添加一个接口的方法,必须要到mabatis.xml配置中心进行注册
11、Lombok的使用
@Data //自动配置get set方法 tostring等
@NoArgsConstructor
@AllArgsConstructor //有参数的构造方法会覆盖无参的构造方法
12、多对一(队形的映射)和一对多(集合的映射)的处理
问题:
1、查询所有学生的信息
2、根据学生的tid,寻找对应的老师 子查询
1、按照查询嵌套处理
<select id="getStudent" resultMap="Student">
select * from student //查询返回学生但是老师的那一列为null
</select>
//修改:必须关联teacher表 //resultMap对应的是结果映射
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
//这里对于student来说,查出的老师属于多对一的关系,在这个多的定义里,所选择的是association
<resultMap id="StudentTeacher" type="Student">
<result property = "id" column = "id"/>
<result property = "name" column = "name"/>
//复杂的属性,我们需要单独处理 对象:association 集合:collection ?JPA
<association property="teacher" column="tid" javatype="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id = #{id}
</select>
总的流程相当于:查询一个学生的信息,其中包含老师,但老师是一个对象,没有所包含的返回值,不能直
接返回一个Student类型,因此使用ResultMap进行结果集映射,对于多对一的关系,返回的是1.则使用的是
assoction,对应的属性值为teacher, 列为tid(是对应的student表中的teacher的列吗??? javaType与对
应的类进行绑定,select,对应的select的方法)
2、按照结果嵌套处理 (这个更简单了点)
<select>
select s.id sid,s.name sname, t.name tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="name" column="tname">
</assocition>
</resultMap>
3、按照结果嵌套处理(一对多的情况)
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname, t.name tname, t.id tid
form student s, teacher t
where s.tid=t.id and t.id=#{tid}
</select>
<resultMap>
<result property="id" column="tid">
<result property="name" column="tname">
//对应的集合
//property对应的实体类的属性,使用ofType进行返回对应的类
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname">
<result property="sid" column="tid">
</collection>
</resultMap>
13、动态的SQL
根据不同的条件生成不同的SQL语句
1、if 标签
//场景使用:比如按照不同的条件进行筛选,则对应不同的if标签
对应的where的条件不同
select * from student
if<test="age>10">
and age=#{age}
</if>
if< test="name != null">
and name=#{name}
</if>
2、where标签
3、foreach标签
4、动态SQL代码片段
14、缓存
一级缓存
SQLsession缓存,与数据库同一次会话期间查询的数据会放在本地的缓存中
以后如果需要获取相同的数据,直接从缓存中去拿,没必要再去查询数据库
缓存失效的情况
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存
- 查询不同的东西
- 查询不同的mapper 就是处理不同接口的方法
- 手动清理缓存
二级缓存
操作:只需要在mapper文件中加入一个标签
开启:随要默认是开启的,可读性较低,在setting标签中,进行设置name=cacheEnabled和value=true
- 全局缓存,基于nameSpace的缓存,即作用的是整个接口中的所有的方法,而不仅仅作用于sqlSeesion对应的只是接口中的一个方法
- 工作机制:
- 一个会话查询一条数据,这个数据就会放在当前会话的一级缓存中
- 如果及绘画关闭了,一级缓存消失,将数据保存到耳机的缓存中
- 新的会话查询的信息,就可以熊二级缓存获取信息
- 不同的mapper查出的数据会放在自己对应的缓存中的
问题:
- 我们需要将实体类进行序列化
- 所有的数据都会先放在一级的缓存中,只有当绘画提交的时候才会将数据保存到二级的缓存
- 当用户刚开始查缓存的时候先查的是二级缓存,看有没有
- 也可以自定义缓存,使用Ehcache
- Pagehelper是Mybatis的分页插件
- 对应的Mybatis-plus也是插件,简化开发
Mybatis面试题
- #{}和{}的使用? 答:**使用#有效可以防止SQL的注入**,类似于preparedStatement和statement. {}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换
- Mybatis是如何进行分页的?分页插件的原理是什么?
答:Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
使用limit实现分页
select * from user limit startIndex,pagesize (开始索引和页面的大小) - Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不? 答:Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签trim|where|set|foreach|if|choose|when|otherwise|bind。
其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。
- Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
答:Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。
延迟加载对应的是多个表的联合查询
- Mybatis中如何执行批处理? 答:使用BatchExecutor完成批处理。