Mybattis 介绍
mybatis 本是apache 的一个开源项目,ibatis ,2010年这个项目由Apache software foundation 迁移到了good了code
,改名为Mybatis 2013年11月迁移到Github。
Mybatis 是一个优秀的持久层框架,他对于Jdbc 的操作数据库的过程进行封装使开发者只需要关注Sql 本身,而不需要在花费精力去处理例如注册驱动,创建connection ,创建statement ,手动设置参数,结果集检索等Jdbc 繁杂的过程代码。
mybatis 通过xml 或者注解的方式将要执行各种statement (statement、preparedStatemnt、CallableStatement)配
置起来并通过java 对象和statement中的sql 进行映射生成最终执行的sql 语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
普通的jdbc 编程步骤
- 加载数据库驱动
- 创建并获取数据库链接
- 创建jdbc statement对象
- 设置sql语句
- 设置sql语句中的参数(使用preparedStatement)
- 通过statement执行sql并获取结果
- 对sql执行结果进行解析处理
- 释放资源(resultSet、preparedstatement、connection)
jdbc问题总结如下:
- 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
- Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码。
- 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
- 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。
Mybatis 框架
mybatis 配置
SqlMapconfig.xml 此文件作为mybatis 的全局配置文件配置了mybatis 的运行环境
mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
- 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
- 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
- mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
- Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
- Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,
- 输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
第一个demo
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/fuxi?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 映射-->
<mappers>
<mapper resource="Mapper.xml"></mapper>
</mappers>
</configuration>
Mapper.xml
<?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">
<!-- namespace:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 -->
<mapper namespace="test">
<!-- 插入的-->
<insert id="insert" parameterType="com.jj.pojo.Vip">
insert into vip (name,pwd) values (#{name},#{pwd})
</insert>
<!-- 全查-->
<select id="show" parameterType="com.jj.pojo.Vip" resultType="com.jj.pojo.Vip">
select * from vip
</select>
<!-- 修改 -->
<update id="update" parameterType="com.jj.pojo.Vip">
update vip set name=#{name} where id=#{id}
</update>
<!-- 删除-->
<delete id="del" parameterType="int">
delete from vip where id=#{id}
</delete>
<!-- 单查-->
<select id="selectone" parameterType="int" resultType="com.jj.pojo.Vip">
select * from vip where id=#{id}
</select>
<!-- 模糊查询-->
<select id="selectlike" parameterType="string" resultType="com.jj.pojo.Vip">
select * from vip where name like #{name}
</select>
</mapper>
log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
Mybatisday01
package Demo;
import com.jj.pojo.Vip;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Mybatisday01 {
private SqlSession sqlSession;
@Before
public void text1() throws IOException {
// 获取xml 的资源
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取创建的工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建工厂
SqlSessionFactory session = builder.build(in);
// 获取到session
sqlSession = session.openSession();
}
@Test
public void demo() throws IOException {
// 调用方法
int i = sqlSession.insert("test.insert", new Vip("jjj","111"));
sqlSession.commit();
sqlSession.close();
if (i>0){
System.out.println("添加成功!!");
}
}
@Test
public void demo2() throws IOException {
// 调用方法
List<Vip> list = sqlSession.selectList("test.show", new Vip());
System.out.println(list);
// sqlSession.commit();
sqlSession.close();
}
// 修改
@Test
public void demo3() throws IOException {
// 调用方法
int i = sqlSession.update("update", new Vip(26, "kiko"));
if (i>0){
System.out.println("修改成功");
}
sqlSession.commit();
sqlSession.close();
}
//删除
@Test
public void demo4(){
// 调用方法
int i = sqlSession.delete("del", 5);
if (i>0){
System.out.println("删除成功!");
}
sqlSession.commit();
sqlSession.close();
}
// 单插
@Test
public void demo5(){
// 调用方法
Vip selectone = sqlSession.selectOne("selectone", 26);
System.out.println(selectone);
sqlSession.close();
}
// 模糊查询
@Test
public void demo6(){
// 调用方法
List<Object> list = sqlSession.selectList("selectlike", "%憨%");
System.out.println(list);
sqlSession.close();
}
}
Mybatis解决jdbc编程的问题
- 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。
解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库链接。
- Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
- 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
- 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。
mybatis与hibernate不同
Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
#{}和${} 的区别
#{}表示一个占位符号,通过#{}可以实现preparedStatement 向占位符中设置值,
自动进行java类型和jdbc 类型转换。#{}可以有效防止sql 注入。#{}可以接收简单类型值或者pojo 属性值。如果parmerType 传输单个简单类型值。#{}括号中可以是value 或者其他名称
是以前servlect 项目中ps 会把sql 参数写成 ?,? 预编译
会把传入的参数(数据)当做一个字符串,会自动对传来的数据加上 "" 只有加 “” 才会更大程度上防止sql 注入
${} 表示拼接sql 串,通过${}可以将parameterType 传入放入内容拼接在sql 中且不进行jdbc 类型转换,${}可以接收简单类型值或者pojo属性如果parameterType传输单个简单类型值,${}括号中只能是value。
传入到数据库中,只会是一个没有加“” 直接 5 或者张三。 就不安全,不防止sql 注入
mybatis 在排序的时候order by 的时候会 用 ${} 而不用#{}
parameterType 和resultType
parameterType:指定输入参数类型,mybatis通过ognl 从输入对象中获取参数值拼接在sql 中
resultType:指定输入结果类型,mybatis 将sql查询结果的一行记录数据映射为resultype 指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器List 中
selectone 和selectlist 区别
selectone 查询一条记录,如果使用selectone 查询多条记录抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectlist 可以查询一条或者多条数据
原始的dao 开发出现的问题
dao方法体存在重复代码:通过sqlsessionFactory 创建sqlsession,调用sqlsession 的数据库操作方法
调用sqlsession 的数据库操作方法需要指定statement 的id ,这里存在硬编码,不得与开发维护
Mapper动态代理方式
开发规范
mapper 接口开发方法只需要写mapper 接口(相当于dao 接口),
由mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体实现类方法
Mapper 接口开发需要遵循以下规范:
- mapper.xml 文件中的namespace 与mapper 接口的类路径相同
- mapper 接口方法名和mapper.xml 中定义的方法名 的id 相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
Mapper1接口
package com.jj.mapper;
import com.jj.pojo.Vip;
import java.util.HashMap;
import java.util.List;
public interface Mapper1 {
// 添加
int inser(Vip vip);
// 全查
List<Vip> selectlist();
// 删除
int del (int id);
// 修改
int update(Vip vip);
// 模糊查询
List<Vip>selectlike(String name);
// 分页查询
List<Vip> pageselect(HashMap map);
//查询一条数据
List<Vip> selectone(String name);
}
Mapper1.xml
<?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">
<!-- namespace:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 -->
<mapper namespace="com.jj.mapper.Mapper1">
<!-- 插入的-->
<insert id="inser" parameterType="com.jj.pojo.Vip">
insert into vip (name,pwd) values (#{name},#{pwd})
</insert>
<!--全查的-->
<select id="selectlist" parameterType="com.jj.pojo.Vip" resultType="com.jj.pojo.Vip">
select * from vip
</select>
<!-- 删除的-->
<delete id="del" parameterType="int">
delete from vip where id=#{id}
</delete>
<!-- 修改-->
<update id="update" parameterType="com.jj.pojo.Vip">
update vip set name=#{name} where id=#{id}
</update>
<!-- 模糊查询 -->
<select id="selectlike" parameterType="string" resultType="com.jj.pojo.Vip">
select * from vip where name like #{name}
</select>
<!-- 分页查询-->
<select id="pageselect" parameterType="hashmap" resultType="com.jj.pojo.Vip">
select * from vip limit #{static},#{offset}
</select>
<!-- 查询一条数据-->
<select id="selectone" resultType="com.jj.pojo.Vip" parameterType="String">
select * from vip where name=#{name}
</select>
</mapper>
测试类
package Demo;
import com.jj.mapper.Mapper1;
import com.jj.pojo.Vip;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
public class Mybatisday02 {
private SqlSession sqlSession;
@Before
public void text1() throws IOException {
// 获取xml 的资源
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取创建的工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建工厂
SqlSessionFactory session = builder.build(in);
// 获取到session
sqlSession = session.openSession();
}
// 插入
@Test
public void demo() throws IOException {
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
int i = mapper.inser(new Vip("jj", "111"));
if (i>0){
System.out.println("添加成功!!");
}
sqlSession.commit();
sqlSession.close();
}
//全查
@Test
public void demo1(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
List<Vip> list = mapper.selectlist();
System.out.println(list);
sqlSession.close();
}
// 删除
@Test
public void demo2(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
int i = mapper.del(24);
if (i>0){
System.out.println("删除成功!!!!");
}
sqlSession.commit();
sqlSession.close();
}
//修改
@Test
public void demo3(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
int i = mapper.update(new Vip(27, "冯娇娇"));
if (i>0){
System.out.println("修改成功!!!!");
}
sqlSession.commit();
sqlSession.close();
}
//模糊查询
@Test
public void demo4(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
List<Vip> list = mapper.selectlike("%憨%");
System.out.println(list);
sqlSession.close();
}
// 分页查询
@Test
public void demo5(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
// 获取hashmap
HashMap<String, Object> map = new HashMap<>();
// 添加参数
map.put("static",0);
map.put("offset",6);
List<Vip> list = mapper.pageselect(map);
System.out.println(list);
sqlSession.close();
}
// 查询一条数据
@Test
public void demo6(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
List<Vip> list = mapper.selectone("冯娇娇");
System.out.println(list);
sqlSession.close();
}
}
动态sql
通过mybatis 提供的各种标签方法实现动态拼接sql.
if 标签
<!-- 动态sql if 标签-->
<select id="ifde" parameterType="vip" resultType="vip">
select * from vip where 1=1
<if test="name !=null and name !=''">
and name=#{name}
</if>
<if test="pwd != null and pwd !=''">
and pwd like '%${pwd}%'
</if>
</select>
接口
// 动态Sql if 的
List<Vip> ifde (Vip vip);
测试类的
//动态Sql if的
@Test
public void demo8(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
Vip vip = new Vip();
vip.setName("娇娇");
vip.setPwd("12");
List<Vip> list = mapper.wherede(vip);
System.out.println(list);
sqlSession.close();
}
where 的
<!-- 动态Sql where 标签-->
<select id="wherede" parameterType="vip" resultType="vip">
select * from vip
<where>
<if test="name !=null">
and name=#{name}
</if>
<if test="pwd != null and pwd !=''">
and pwd like '%${pwd}%'
</if>
</where>
</select>
接口测试差不多
foreach标签
xml
<!-- foreach 标签 ,批量添加-->
<insert id="insertbyid" parameterType="map">
insert into vip (name,pwd) values
<foreach collection="viplist" separator="," item="vip">
(#{vip.name},#{vip.pwd})
</foreach>
</insert>
接口
// 批量添加
int insertbyid(Map map);
测试类
// 批量添加
@Test
public void demo9(){
// 调用方法
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
List<Vip> list = new ArrayList();
for (int i = 0; i < 5; i++) {
Vip vip = new Vip();
vip.setName("娇娇"+i);
vip.setPwd("1111"+i);
list.add(vip);
}
HashMap map = new HashMap();
map.put("viplist",list);
int i = mapper.insertbyid(map);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
mybatis 生命周期
生命周期作用域是至关重要的,因为错误的使用会导致非常严重的并发问题
SqlsessionFactoryBuilder:
一旦创建了SqlsessionFactory ,就不在需要他了,局部变量
SqlsessionFactory:
可以想象为数据库连接池SqlsessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它,或重新创建另一个实例
因此他的最佳作用域是应用作用域
最简单的就是使用单例模式或者静态单例模式
Sqlsession:
连接到连接池的一个请求!
sqlsession 的实例不是线程安全的,因此是不能被共享的,所以他的最佳作用域是请求或方法作用域
用完记得关闭,避免资源的浪费
ResultMap
用来处理我们实体类字段与数据库不一致的时候用的
上面的方法是最笨的方法
我们也可以用resultMap
日志
第一种
这个是标准的日志
log4j 的使用
导入依赖
<!-- 日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
写log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
实现mybatis 的分页
第一种limit 分页
sql 部分
<!--vipLimit 分页-->
<select id="vipLimit" parameterType="map" resultMap="vipmap">
select * from vip limit #{start},#{end}
</select>
接口
//limit 分页
List<Vip> vipLimit(Map<String,Integer> map);
测试类
//分页
@Test
public void demo1(){
Mapper1 mapper = sqlSession.getMapper(Mapper1.class);
Map<String, Integer> map = new HashMap<>();
map.put("start",0);
map.put("end",10);
List<Vip> vips = mapper.vipLimit(map);
for (Vip vip : vips) {
System.out.println("vip = " + vip);
}
sqlSession.close();
}
结果
RowBounds 分页 了解
sql
<!-- 分页2-->
<select id="RowBoundsLimit" resultMap="vipmap">
select * from vip
</select>
接口
// RowbansLimit 分页
List<Vip> RowBoundsLimit();
测试类
//分页
@Test
public void demo2(){
//获取Rowbans
RowBounds rowBounds = new RowBounds(0, 1);
List<Vip> list = sqlSession.selectList("com.jj.mapper.Mapper1.RowBoundsLimit", null,rowBounds);
for (Vip vip : list) {
System.out.println("vip = " + vip);
}
// System.out.println("list = " + list);
sqlSession.close();
}
结果
分页插件,我在另一个博客写过了
分页插件使用
Mybatis 执行流程
多对一,一对多的
数据库的设计
实现的方式一
我们可以用子查询的理解来做,就是按照查询嵌套处理
实体类
Student
import lombok.*;
/**
* @author fjj
* @date 2021/1/28 15:02
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int id;
private String name;
private int age;
private teacher teacher;
}
teacher 的
package com.jj.pojo;
import lombok.*;
/**
* @author fjj
* @date 2021/1/28 15:00
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class teacher {
private int id;
private String name;
}
我们的xml
<select id="getStudent" resultMap="student1">
select * from student
</select>
<resultMap id="student1" type="student">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<association property="teacher" column="tid" select="teacher"></association>
</resultMap>
<select id="teacher" resultType="teacher" parameterType="int">
select * from teacher where id=#{id}
</select>
这里我们的思路是,我们可以先查询出来Student 表的所有数据,然后再查询出来老师表的。大概就是用子查询的思想
接口
//查询学生信息
List<Student> getStudent();
测试类
//多对一查询1
@Test
public void demo3(){
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> list = mapper.getStudent();
for (Student student : list) {
System.out.println("student = " + student);
}
sqlSession.close();
}
结果的
方式2
按照结果查询
接口不变,唯一变得是我们得xml
<!-- 多对一查询2 -->
<select id="getStudent1" resultMap="getStudent1">
select s.*,t.name tname from student s INNER JOIN teacher t on s.tid=t.id
</select>
<resultMap id="getStudent1" type="student">
<result property="id" column="id"></result>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<association property="teacher" javaType="teacher">
<result property="name" column="tname"></result>
</association>
</resultMap>
结果
一对多得
方式一,根据结果查询
实体类
package com.jj.pojo;
import lombok.*;
import java.util.List;
/**
* @author fjj
* @date 2021/1/28 15:00
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class teacher {
private int id;
private String name;
private List<Student> students;
}
package com.jj.pojo;
import lombok.*;
/**
* @author fjj
* @date 2021/1/28 15:02
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private int id;
private String name;
private int age;
private int tid;
// private teacher teacher;
}
xml
<select id="getStudent" resultMap="getStudent1">
SELECT t.*,s.name ,s.age from teacher t inner JOIN student s on s.tid=t.id AND t.id=#{id}
</select>
<resultMap id="getStudent1" type="teacher">
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<collection property="students" ofType="student">
<result property="tid" column="tid"></result>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="id" column="id"></result>
</collection>
</resultMap>
接口
package com.jj.mapper;
import com.jj.pojo.Student;
import com.jj.pojo.teacher;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author fjj
* @date 2021/1/28 15:13
*/
public interface teacherMapper {
//获取学生
List<teacher> getStudent(@Param("id") int id);
}
测试类
//多一对多查询1
@Test
public void demo5(){
teacherMapper mapper = sqlSession.getMapper(teacherMapper.class);
List<teacher> list = mapper.getStudent(1);
for (teacher teacher : list) {
System.out.println(teacher+"\t");
}
sqlSession.close();
}
结果
按照子查询得方式查询
xml
<!-- 根据子查询得-->
<select id="getStudent1" resultMap="getStudent2">
select * from teacher where id=#{id}
</select>
<resultMap id="getStudent2" type="teacher">
<collection property="students" ofType="student" javaType="ArrayList" column="id" select="student111">
</collection>
</resultMap>
<select id="student111" resultType="student">
select * from student where tid=#{id}
</select>
其他得都一样,两个都可以用哪个比较顺手,就用那个就可以!
Mybatis 缓存
缓存得概念
存在内存中得临时数据
将用户经常查询得数据放到缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高效率,解决了高并发系统得性能问题
为什么使用缓存?
减少和数据库得交互次数,减少系统开销,提高系统效率
什么样得数据能使用缓存?
经常查询并且不经常改变得数据
Mybatis 缓存
Mybais 包含一个非常强大得查询缓存特性,它非常方便地定制和配置缓存,缓存可以极大得提升查询效率
Mybatis 系统中默认定义了两级缓存:一级缓存和二级缓存
默认情况下,只有一级缓存开启。(Sqlsession 级别得缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace 级别得缓存
为了提高扩展性,Mybatis 定义了缓存接口Cache 我们可以通过实现Cache 接口来自定义二级缓存
一级缓存
当我们在执行一条查询语句得时候
一级缓存失效得几种方式
一,我们可以在中间加一个sqlSession.clearCache();
二
3,在中间加一个增删改得方法,
4,其他就是不在一个Mapper 得情况了
二级缓存
二级缓存也叫全局缓存,一级缓存得作用域太低了,所以诞生了二级缓存
基于namespace 级别得缓存,一个名称空间,对应一个二级缓存
工作机制
一个会话查询一条数据,这个数据就会被放在当前会话一级缓存中
如果当前会话关闭了,这个会话对应得一级缓存就死了,我们想要得是当一级缓存关闭了,一级缓存得数据会保存到二级缓存中
新的会话查询信息,就可以从二级缓存中获取内容
不同得mapper 查出得数据会放到自己对应得缓存中
我们先在配置文件里开启缓存
在mapper xml 里开启
测试类
结果
Mybatis整合spring
整合思路
- SqlSessionFactory对象应该放到spring容器中作为单例存在。
- 传统dao的开发方式中,应该从spring容器中获得sqlsession对象。
- Mapper代理形式中,应该从spring容器中直接获得mapper的代理对象。
- 数据库的连接以及数据库连接池事务管理都交给spring容器来完成。
导入需要的jar
- spring的jar包
- Mybatis的jar包
- Spring+mybatis的整合包。
- Mysql的数据库驱动jar包。
- 数据库连接池的jar包。
加入配置文件
sqlmapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 设置别名 -->
<typeAliases>
<!-- 2. 指定扫描包,会把包内所有的类都设置别名,别名的名称就是类名,大小写不敏感 -->
<package name="com.jj.pojo" />
</typeAliases>
</configuration>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!--使用c3p0连接池配置数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--下面还可以配置连接数量等-->
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置mybatis核心配置文件 -->
<property name="configLocation" value="classpath:sqlmapConfig.xml" />
<!-- 配置数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Mapper代理的方式开发方式一,配置 Mapper 代理对象-->
<bean id="usermapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!--配置mapper 接口-->
<property name="mapperInterface" value="com.jj.dao.Vipmapper"/>
<!-- 配置sqlsessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///fuxi
jdbc.username=root
jdbc.password=123456
log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- Mapper代理形式开发dao
Vipmapper.xml
<?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.jj.dao.Vipmapper">
<!--查询-->
<select id="selectall" resultType="vip">
select * from vip
</select>
<!-- 添加-->
<!-- foreach 标签 ,批量添加-->
<insert id="insertbyid" parameterType="map">
insert into vip (name,pwd) values
<foreach collection="viplist" separator="," item="vip">
(#{vip.name},#{vip.pwd})
</foreach>
</insert>
</mapper>
Vipmapper
package com.jj.dao;
import com.jj.pojo.Vip;
import java.util.List;
import java.util.Map;
public interface Vipmapper {
List<Vip> selectall();
// 批量添加
int insertbyid(Map map);
}
测试类
import com.jj.dao.Vipmapper;
import com.jj.pojo.Vip;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Demo1 {
private ApplicationContext context;
@Before
public void setUp() throws Exception {
this.context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
@Test
public void springmybatis(){
//获取Mapper
Vipmapper bean = this.context.getBean(Vipmapper.class);
List<Vip> list = bean.selectall();
System.out.println(list);
}
@Test
public void springmybatis1(){
//获取Mapper
Vipmapper bean = this.context.getBean(Vipmapper.class);
List<Vip> list = new ArrayList();
for (int i = 0; i < 5; i++) {
Vip vip = new Vip();
vip.setName("憨憨"+i);
vip.setPwd("1111"+i);
list.add(vip);
}
HashMap map = new HashMap();
map.put("viplist",list);
int i = bean.insertbyid(map);
if (i>0){
System.out.println("添加成功!!");
}
}
}