目录
一对一多表查询(也就是单表查询):association一对一 javaType="一个对象"
一,简介
1.什么是mybatis
Mybatis是一个半ORM(对象关系映射)的持久层框架,内部封装了JDBC,可以通过xml配置或注解来配置和映射原生信息
二,框架搭建和xml配置
1.框架搭建
(1)导入jar包(build libraries)或Maven引入依赖
(2)创建核心配置类mybatis.xml(mybatis-config.xml)
事务管理设置:
JDBC管理事务
MANAGED: mybatis不管事务管理,交给容器(spring)来管理事务
<!-- 事务管理器-->
<transactionManager type="JDBC"></transactionManager>
数据源设置:POOLED | UNPOOLED | JNDI
POOLED :采用数据库连接池,来创建数据库链接。
UNPOOLED:不采用数据库连接池。
JNDI :在外部配置数据源,然后放置一个 JNDI 上下文的引用,通过名字引入这个外部的数据源
<!-- 数据源-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/saima?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>
模板:环境可以配置多个
<?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>
<!-- 1、数据库环境-->
<environments default="mysql8.x">
<environment id="mysql8.x">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///hscrm?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<!-- <environment id="mysql5.x">-->
<!-- <transactionManager type=""></transactionManager>-->
<!-- <dataSource type=""></dataSource>-->
<!-- </environment>-->
<!-- <environment id="oracle11g">-->
<!-- <transactionManager type=""></transactionManager>-->
<!-- <dataSource type=""></dataSource>-->
<!-- </environment>-->
</environments>
<!-- 2、加载映射文件-->
<mappers>
</mappers>
</configuration>
三.实体,接口,和接口的映射文件(规范)
1.编写实体类 如:Student
2.编写Mapper接口层(dao)如:StudentMapper或StudentDao
3..编写映射文件StudentMapper.xml(XML文件用于映射)
namespace:映射接口的路径
id:接口的方法名
resultType:返回的类型
<?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.hs.mapper.CustomerMapper">
<select id="findAllCustomer" resultType="com.hs.domain.Customer">
select * from customer
</select>
</mapper>
四,#和$的区别
#:底层使用的是PeraredStatement,可以使用占位符,避免sql拼接
如:where password = '123 or 1=1 '
$:底层使用Statement
五,封装工具类
创建一个工具类,将重复性的代码封装在一个类中调用
package com.hs.util;
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 java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory ssf;
static {
// 配置文件
String fileName="mybatis.xml";
InputStream in= null;
try {
in = Resources.getResourceAsStream(fileName);
} catch (IOException e) {
e.printStackTrace();
}
// 会话工厂(只需构建一次)
ssf= new SqlSessionFactoryBuilder().build(in);
}
public static SqlSession getSqlSession(){
return ssf.openSession();
}
public static void closeSqlSession(SqlSession sqlSession ){
sqlSession.commit();
sqlSession.close();
}
}
六,Mybatis的执行流程
七,传参和手动结果映射
1.传参
通常把多个参数封装成map或实体
方法一:通过#{arg0}获取顺序的参数
<select id="findCustomerByNameAndSex" resultType="com.hs.domain.Customer">
select * from customer where c_name=#{arg0} and c_sex=#{arg1}
</select>
方法二:通过#{param1}获取顺序的参数
<select id="findCustomerByNameAndSex" resultType="com.hs.domain.Customer">
select * from customer where c_name=#{param1} and c_sex=#{param2}
</select>
方法三:在各个参数前使用@Param("参数名")设置参数的名称
List<Customer> findCustomerByNameAndSex(@Param("name") String c_name, @Param("sex") String c_sex);
<select id="findCustomerByNameAndSex" resultType="com.hs.domain.Customer">
select * from customer where c_name=#{name} and c_sex=#{sex}
</select>
2.手动结果映射
问题:当表字段与实体属性不一致时如何解决
方法一:使用sql语句中起别名的方式 as 字段名与实体属性名一致。
方法二:使用mybatis中的结果集手动映射
<resultMap id="aaaa" type="映射的实体类">
<!-- id为主键 -->
<id property="实体的属性" column="对应的字段"></id>
<result property="实体的属性" column="字段"></result>
</resultMap>
<!-- resultMap对应的是aaaa -->
<select id="映射的接口方法名" resultMap="aaaa">
select * from student
</select>
九,多表查询和动态sql
1.多表查询
表与表之间的关系:
一对一(一个人只有一张身份证,一个身份证只能对应一个人)
一对多(一个人可以办理多张银行卡,一张银行卡只能对应一个人)
多对多(通过中间表,拆为两个一对多)(一个学生可以选择多门课程,一个课程可以由多个学生学习)
一对一多表查询(也就是单表查询):association一对一 javaType="一个对象"
方法一:根据表连接的查询结果直接属性与字段对应
public class Student {
private int sid;
private String sname;
// 一对一 电脑的属性
private int cid;
private String cname;
方法二:将另一个关联的表当作一个属性
public class Computer {
private int cid;
private String cname;
public class Student {
private int sid;
private String sname;
//每个学生对应一个电脑
private Computer computer;
/**
* 查询所有学生的姓名和电脑名称
*/
List<Student> findAllStudent();
<resultMap id="sc" type="com.hs.domain.Student">
<id property="sid" column="sid"></id>
<result property="sname" column="sname"></result>
<association property="computer" column="cid" javaType="com.hs.domain.Computer">
<id property="cid" column="cid"></id>
<result property="cname" column="cname"></result>
</association>
</resultMap>
<select id="findAllStudent" resultMap="sc">
select * from student s,computer c where s.cid=c.cid;
</select>
方法三:使用mybatis推荐的
public class Person {
private int pid;
private String pname;
// private int cid; 一对一 身份证
private IdCard idCard;
public class IdCard {
private int cid;
private String cnum;
//IdCard中的查询com.saima.mapper.IdCardMapper.findIdCard
<mapper namespace="com.saima.mapper.IdCardMapper">
<select id="findIdCard" resultType="idCard">
select * from idcard where cid = #{cid}
</select>
</mapper>
//Person中的查询
<resultMap id="fpid" type="person">
<id property="pid" column="pid"></id>
<result property="pname" column="pname"></result>
<!-- 通过select的标签属性来连接idCard的查询语句,然后映射-->
<association property="idCard" column="cid" javaType="idCard" select="com.saima.mapper.IdCardMapper.findIdCard">
<id property="cid" column="cid"></id>
<result property="cnum" column="cnum"></result>
</association>
</resultMap>
<select id="findPersonAndIdCard" resultMap="fpid">
select * from person
</select>
一对多:collection 一对多 ofType=""
方法一:和一对一的方式一样,但会产生冗余性代码........(有兴趣敲一下)
方法二:将另一个关联的表当作一个集合属性
public class Clazz {
private int clsid;
private String clsname;
//一个班级对应多个学生
private List<Student> studentList;
<resultMap id="cs" type="com.hs.domain.Clazz">
<id property="clsid" column="clsid"></id>
<result property="clsname" column="clsname"></result>
<collection property="studentList" ofType="com.hs.domain.Student">
<id property="sid" column="sid"></id>
<result property="sname" column="sname"></result>
</collection>
</resultMap>
<select id="findAllStudentInClass" resultMap="cs" parameterType="int">
select * from clazz c,student s where c.clsid=s.clsid and c.clsid=#{clsid}
</select>
方法三:mybatis推荐的方式
public class Person {
private int pid;
private String pname;
// private int pid; 一对多 对应多个BankCard卡
private List<BankCard> bankCardList;
public class BankCard {
private int bid;
private String bnum;
private double yueee;
<mapper namespace="com.saima.mapper.BankCardMapper">
<select id="findBankCard" resultType="bankCard">
select * from bankcard where pid = #{pid}
</select>
</mapper>
<resultMap id="fpbc" type="person">
<id property="pid" column="pid"></id>
<result property="pname" column="pname"></result>
<collection property="bankCardList" column="pid" ofType="bankCard" select="com.saima.mapper.BankCardMapper.findBankCard">
<id property="bid" column="bid"></id>
<result property="bnum" column="bnum"></result>
<result property="yueee" column="yuee"></result>
</collection>
</resultMap>
<select id="findPersonAndBankCard" resultMap="fpbc">
select * from person
</select>
2.动态sql
使用场景:多多条件查询,sql拼接,修改操作。
通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句(自定义sql)
where标签添加where 关键字,并且可以去除开头的and 和 or 。
set标签用于添加set关键字,还可以去除前后多余的逗号。 choose标签多条件查询,每次只执行一种结果。 trim标签可以代替where和set标签。 foreach标签可以循环遍历集合查询,通常用于批量查询。
十,sql片段,日志,缓存
1.sql片段
定义sql片段:使用sql标签
引用sql片段:include标签
<sql id="queryuser">
select * from user
</sql>
<select id="findUserByUsernameOrSex" parameterType="com.hs.domain.User" resultType="com.hs.domain.User">
<include refid="queryuser"></include>
<trim prefix="where" prefixOverrides="and">
<if test="username != null">
username=#{username}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</trim>
</select>
2.日志
日志:就是记录程序的运行轨迹,方便查找关键信息,快速定位错误解决问题,日志可以打印在console控制台中,也可以写入日志文件*.log中。
框架都支持日志:spring框架支持日志,springMVC支持日志,mybatis也支持日志
日志组件:
日志对象:logger
日志级别:OFF 关闭:最高级别,不输出日志。
FATAL 致命:输出非常严重的可能会导致应用程序终止的错误。
ERROR 错误:输出错误,但应用还能继续运行。
WARN 警告:输出可能潜在的危险状况。
INFO 信息:输出应用运行过程的详细信息。
DEBUG 调试:输出更细致的对调试应用有用的信息。
TRACE 跟踪:输出更细致的程序运行轨迹。
ALL 所有:输出所有级别信息。
常用的是:ERROR,WARN,INFO,DEBUG。
常见的日志api:
log4j,log4j2,logback==
slf4j提供了统一的日志接口:
使用:(1)导入jar包
(2)log4j.properties日志文件
#日志配置
log4j.rootLogger = DEBUG, stdout,fl
#输出到控制台
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %5p [%t] - %m%n
#输出到日志文件
log4j.appender.fl = org.apache.log4j.DailyRollingFileAppender
log4j.appender.fl.File=D:\\fl.log
log4j.appender.fl.layout = org.apache.log4j.PatternLayout
#日志信息可以自己配
log4j.appender.fl.layout.ConversionPattern = %5p [%t] - %m%n
十一,一些常用的配置
mybatis别名:
<!-- 起别名:方式1-->
<typeAliases>
<typeAlias type="com.rui.pojo.User" alias="User"/>
</typeAliases>
<!-- 起别名:方式2-->
<typeAliases>
<package name="com.hs.domain"/>
</typeAliases>
十二,延迟加载(懒加载)
针对多表关联查询(mybatis官方推荐的写法)(鸡肋)
不需要查询另一个表的数据时,就不去调用另一个表的查询
配置:
全局延迟加载:在mybatis.xml核心配置文件中加入,测试多表查询(不调用关联表的属性)
<!-- 开启全局延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- true只要加载一个属性,其他属性也加载,false只加载需要的属性,其他属性不加载-->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 执行哪些方法会导致立即加载,默认值equals,clone,hashCode,toString-->
<setting name="lazyLoadTriggerMethods" value=""/>
局部延迟加载:在接口映射文件的手动映射上association标签中加入属性fetType
(1)注释全局加载设置
(2)设置局部延迟加载
lazy:代表延迟加载
eager:代表饥饿加载(没有用延迟加载,调用接口表和关联表都会查询)
<association fetchType="lazy" ...
<collection fetchType="lazy" ...
十三:缓存(cache)
介绍:针对查询操作
对数据库的查询操作,保存到内存中,下次查询相同的sql语句,直接在缓存中获取。
缓存失效:
增删改操作会删除缓存,session flush或close都会删除缓存。
一级缓存:
一级缓存默认开启,作用域为session级别
二级缓存:
二级缓存默认关闭,需手动打开,作用域为sessionFactory级别
实现:(1)在mybatis.xml核心配置文件中加入配置
<!-- 开启二级缓存-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
(2)在需要的接口映射文件中加入
<mapper namespace="com.saima.mapper.StudentMapper">
<!-- 二级缓存-->
<cache/>
<select id="findStu" resultType="student">
select * from student
</select>
</mapper>
二级缓存技术:
也可以采用第三方的二级缓存技术:ehcache,memcache,redis==
十四:数据库连接池(Druid)
采用第三方的数据库连接池(不使用mybatis的数据库连接池)
(1)导入jar包
(2)配置druid数据源
<dataSource type="com.hs.util.DruidDataSourceFactory">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///hscrm?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
<!--配置初始化连接池大小、最小连接数、最大连接数。-->
<property name="initialSize" value="5" />
<property name="minIdle" value="10" />
<property name="maxActive" value="20" />
<!--配置获取连接等待超时的时间。-->
<property name="maxWait" value="60000" />
<!--配置一个连接在连接池中的最小生存时间、最大生存时间,超过最大生存时间会被移除,单位毫秒。-->
<property name="minEvictableIdleTimeMillis" value="600000" />
<property name="maxEvictableIdleTimeMillis" value="900000" />
(3)自定义数据源工厂
public class DruidDataSourceFactory2 extends UnpooledDataSourceFactory {
public DruidDataSourceFactory2(){
this.dataSource=new DruidDataSource();
}
@Override
public DataSource getDataSource() {
try {
((DruidDataSource)this.dataSource).init();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return this.dataSource;
}
}
十五:逆向工厂(代码生成器)
MybatisX的使用
(1)idea中下载插件MybatisX
(2)新建一个项目,使用idea连接数据库,测试连接。
(3)选中表,右键点击MybatisX-Generator
(4)配置生成代码在模块,父类包名,默认的和MP等
十六:注解
常用的注解:直接在接口上以注解的方式写sql,需要在核心配置文件中映射接口的包。
@Select
@Update
@Insert
@Delete
@Select
动态Sql:
@SelectProvider:
sql类:
public class StudentSql {
public String findStudents(final Student student){
//动态sql
return new SQL(){
{
SELECT("*");
FROM("student");
if(student.getSname()!=null){
OR().WHERE("sname=#{sname}");
}
if(student.getCid()!=0){
OR().WHERE("cid=#{cid}");
}
if(student.getClsid()!=0){
WHERE("clsid=#{clsid}");
}
}
}.toString();
}
}
常见问题:if判断的是包装类还是基本数据类型 否则会出现nullpointException
如:int 就判断0 Integer 就判断 null
注解接口写的方式:
@SelectProvider(type = StudentSql.class,method = "findStudents")
List<Student> findStudents(Student student);
其他注解就不做过多介绍!