1、什么是框架?
它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题。
使用框架的好处:
框架封装了很多细节,是开发者可以使用极简的方式实现功能。大大提升开发效率。
2、三层架构
表现层:适用于展示数据
业务层:事处理业务需求
持久层:是和数据库交互的
3、持久层技术解决方案
JDBC技术:
Connection
PreparedStatement
ResultSet
Spring的JDBCTemplate:
Spring中对JDBC的简单封装
Apache的JDBCUtils:
它和Spring的JDBCTemplate很像,也是对JDBC的简单封装
以上这些都不是框架
JDBC是规范
Spring的JDBCTemplate和Apache的DBUtils都只是工具类
4、MyBatis的概述
MyBatis是一个持久层框架,用Java语言编写
它封装了JDBC操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建链接等繁杂过程
他使用了ORM思想实现了结果集的封装
ORM: Object Relational Mapping 对象关系映
简单的说:
就是把数据库表和实体类及实体类的属性对应起来
让我们可以操作实体类就实现操作数据库表
user User
id UserID
user_name userName
今天我们需要做到
实体类中的属性和数据库表的字段名称保持一致
user User
id id
user_name user_name
5、MyBatis的入门
mybatis的环境搭建
第一步:创建maven工程并导入坐标
第二步:创建实体类和dao的接口
第三步:创建MyBatis的主配置文件SqlMapConfig.xml
第四步:创建映射配置文件IUserDao.xml
环境搭建的注意事项
第一个:创建IUserDao.xml和创建IUserDao.java时,名称是为了和我们之前的只是保持一致。
在MyBatis中它把持九层的操作接口名称和映射文件也叫做:Mapper
所以,IUserDao和IUserMapper是一样的
第二个:在idea创建目录时,它和包是不一样的
包在创建时:com.xin.dao是三级结构
目录在创建时:com.xin.dao是一级结构
第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同
第四个:映射配置文件的mapper标签Namespace属性的取值必须是dao接口的全限定类名
第五个:映射配置文件的操作配置(select),id属性的取值必须dao接口是方法名
当我们遵从了第三、四、五点之后,我们在开发中就无需再写dao的实现类
mybatis的入门案例
//1、读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2、创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3、使用工厂产生SqlSession对象
SqlSession session = factory.openSession();
//4、使用SqlSession创建dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5、使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user : users){
System.out.println(user);
}
//6、释放资源
session.close();
in.close();
注意事项:不要忘记再映射配置中告知mybatis要封装的实体类中
配置的方式:指定实体类的全限定类名
mybatis基于注解的入门案例
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定sql语句
同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名
明确:
我们在实际开发中都是越简便越好,所以都是采用不写dao实现类的方式
不管使用xml还是注解配置
但是MyBatis它是支持写dao实现类的
代码解析:
读取路径(一般不使用绝对路径和相对路径):
第一个:使用类加载器,他只能读取类路径的配置文件
第二个:使用ServletContext对象的getRealPath()
创建工厂mybatis使用了构建者模式,builder就是构建者,优势:把对象的创建细节隐藏,使使用者直接调用方法即可拿到对象
生产SqlSession使用了工厂模式,优势:解耦(降低类之间的依赖关系)
创建Dao接口实现类使用了代理模式,优势:不修改源码的基础上对已有方法增强
6、自定义MyBatis的分析:
mybatis在使用代理dao的方式实现增删改查是做什么事呢?
只有两件事:
第一:创建代理对象
第二:在代理对象中调用selectList
自定义MyBatis能通过入门案例看到类
class Resources
class SqlSessionFactoryBuilder
interface SqlSessionFactory
interface SqlSession
链接数据库的信息,有了他们就能创建Connection对象
<!--配置连接数据库的基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
映射的配置文件信息
<mappers>
<mapper resource="com/xin/dao/IUserDao.xml"></mapper>
</mappers>
执行的SQL语句,就可以获取PreparedStatement和封装实体类的全限定类名
<select id="findAll" resultType="com.xin.domain.User">
select * from user;
</select>
读取配置文件:用到的技术就是解析xml的技术
此处用到的就是***dom4j解析xml技术***
selectList方法
1、根据配置文件的信息创建Connection对象
注册驱动,获取链接
2、获取预处理对象PreparedStatement
此时需要SQL语句
connection.prepareStatement(sql);
3、执行查询
Resultset resultset = preparedStatement.excuteQuery();
4、遍历结果集用于封装
List<E> list = new ArrayList();
while(resultSet.next()){
E element = (E)Class.forName(配置的权限的类名).newInstance();
进行封装,把每个rs的内容都添加到element中
我们的实体类属性和表中的列名是一致的。于是我们就可以把表的类名看成实体类的属性名
就是可以使用反射的方式来根据名称获取每个属性,并赋值(此处使用反射封装)
把element加入到list中
list.add(element);
}
5、返回list
return list;
要想让该selectList方法执行,我们需要给方法提供两个信息
1、连接信息
2、映射信息
两个部分:
第一:执行的SQL语句
第二:封装结果的实体类全限定类名
合起来定义为一个对象Mapper
Mapper的对象:String sql和String domainClassPath
sql为findAll方法的sql语句
domainClassPath为全限定类名
//4、使用SqlSession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
根据dao接口的字节码创建dao的代理对象
public <T> T getMapper(Class<T> daoInterfaceClass){
/**
类加载器:他使用的和被代理对象是相同的类加载器
代理对象要实现的接口:和被代理对象实现相同的接口
如何代理:他就是增强的方法,我们需要自己来提供
此处就是一个InvocationHandle的接口,我们需要写一个该接口的实现类
在实现类中调用selectList方法
*/
Proxy.newProxyInstance(类加载器,代理对象要实现的接口字节码数组,如何代理)
}
第一步:SqlSessionFactoryBuilder接收SqlMapConfig.xml文件流,构建出SqlSessionFactory对象
第二步:SqlSessionFactory读取SqlMapConfig.xml中链接数据库和mapper的映射信息。用来生产出真正操作数据库的SqlSession对象。
第三步:
SqlSession对象有两大作用:
1、生成接口代理对象
2、定义通用的增删改查方法
注:无论哪个分支,除了链接数据库信息,还需要得到sql语句
第四步:
作用一:在在SqlSessionImpl对象的getMapper方法中分两步来实现。
1、先用SqlSessionFactory读取数据库链接信息创建Connection对象
2、通过jdk代理模式创建出代理对象作为getMapper方法返回值,这里主要工作是在创建代理对象是第三个参数处理类里面得到sql语句。执行对应CRUD操作。
作用二:在SqlSessionImpl对象中提供selectList()方法,【当然,实际mybatis框架中还有selectOne,insert等方法】这些方法内也分两步
1、用SqlSessionFactory读取数据库链接信息创建Connection对象
2、直接得到sql语句,使用JDBC的Connection对象进行相应的CRUD操作
第五步:
封装结果集:无论使用分支一生成代理对象,还是直接用分支二提供CRUD方法,我们都需要对返回的数据库结果集进行封装,变成Java对象返回给调用者。所以我们还必须要知道调用者所需要的返回类型
7、OGNL表达式
Object Graphhic Navigation Language
对象 图 导航 语言
他是通过对象的取值方法来获取数据。在写法上把get省略了。
比如:获取用户的名称
类中的写法:user.getUserName();
OGNL表达式写法:user.username
mybatis中为什么能直接写username,而不用user.呢?
因为在paramentType中已经提供了属性所属的类,所以此时不需要写对象名
8、mybatis中的连接池以及事务控制
1、连接池
我们在实际开发中都会使用连接池
因为他可以减少我们获取连接所消耗的时间。
连接池就是用于存储的一个容器
容器其实就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一连接
该集合还必须实现队列的特性:先进先出
2、mybatis中的连接池
mybatis中的连接池提供了3种方式配置:
配置的位置:
主配置文件:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式
type属性的取值:
POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是并没有使用池的思想
JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到的DataSource是不一样的
注意:如果不是web或者maven的war工程,是不能使用的
我们所使用的是tomcat服务器,采用连接池就是dbcp连接池。
3、mybatis的事务
什么是事务?
事务的四大特性ACID
不考虑隔离性会产生的3个问题
解决办法:四种隔离级别
它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚
4、mybatis中的多表查询
表之间的关系有几种:
一对多
多对一
一对一
多对多
举例:
用户和订单就是一对多
订单和用户就是多对一
一个用户可以下多个订单
多个订单属于同一个用户
任何身份证号就是一对一
一个人只能有一个身份证号
一个身份证号只能属于一个人
学生和老师之间就是多对多
一个学生可以被多个老师教
一个老师可以教多个学生
特例:
如果拿出每一个订单,他都只能属于一个用户。
所以mybatis就把多对一看成了一对一
mybatis中的多表查询
事例:用户和高糊
一个用户可以有多个账户
一个账户只能属于一个用户(多个账户也可以属于一个用户)
步骤:
1、简历两张表:用户表、账户表
让在用户表和账户表之间具备一对多的关系:需要使用外键在账户中添加
2、简历两个实体类:用户实体类和账户实体类
让用户和账户的实体类能体现出来一对多的关系
3、简历两个配置文件
用户的配置文件
账户的配置文件
4、实现配置
当我们查询用户时,可以同时得到用户下面所包含的账户信息
当我们查询账户时,可以同时得到账户的所属用户信息
事例:用户和角色
一个用户有多个角色
一个角色赋予多个用户
步骤:
1、简历两张表:用户表、角色表
让在用户表和角色表之间具备多对多的关系:需要使用中间表,中间表中包含各自的主键
,在中间表中是外键
2、简历两个实体类:用户实体类和角色实体类
让用户和角色的实体类能体现出来多对多的关系
各自包含对方一个集合引用
3、简历两个配置文件
用户的配置文件
角色的配置文件
4、实现配置
当我们查询用户时,可以同时得到用户下面所包含的角色信息
当我们查询角色时,可以同时得到角色的所赋予用户信息
补充:
*1、JNDI
Java Naming and Directory Interface: java命名和目录接口是SUN公司提供的一种标准的Java命名系统接口
9、mybatis中的延迟加载
问题:
在一对多中,当我们有一个用户有一百个账户
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的?
在查询账户是,账户的所属信息应该是随着账户查询时一起查询出来。
什么是延迟加载
在真正是用数据时才发起查询,不用的时候不查询。按需查询(懒查询)
什么是立即加载
不管用不用,只要一调用,马上发起查询。
对应的四中表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。
多对一,一对一:通常情况下我们都是采用立即加载。
<!--配置参数-->
<settings>
<!--开启mybatis支持延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
10、mybatis中的缓存
什么是缓存
存在于内存的临时数据
为什么使用缓存
减少和数据库的交互次数,提高执行效率
什么样的数据能使用缓存,什么样的数据不能使用缓存
适用于缓存:
经常查询并且不改变的
数据的正确与否对最终结果影响不大的
不适用于缓存
经常改变的数据
数据的正确与否对最终结果影响很大的
例如:商品的库存,银行的汇率,股市的牌价
MyBatis中的一级缓存和二级缓存
一级缓存:它指的是MyBatis中SqlSession对象的缓存
当我们执行查询之后,查询结果会同时存入到SqlSession为我们提供的一块区域中,
该区域的结构有一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有, 有的话直接拿出来。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
二级缓存:它指的是MyBatis中SqlSesscionFactory对象的缓存。由同一个SqlSessionFactory对象创建的 SqlSesssion共享其缓存。
二级缓存的使用步骤:
第一步:让mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
第二步:让当前映射文件支持二级缓存(在IUserDAO.xml中配置)
第三部:让当前操作支持二级缓存(在select标签中配置)
二级缓存中存放的是数据,而不是对象
{“id”, "username":"老王", "address":"北京"}
11、mybatis的注解开发
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@ResultMap:实现引用@Results定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
@SelectProvider: 实现动态SQL映射
@CacheNamespace:实现注解二级缓存的使用
环境搭建
pom.xml导入的依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
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>
<!--引入外部配置文件-->
<properties resource="jdbcConfig.properties"></properties>
<!--配置别名-->
<typeAliases>
<package name="com.xin.domain"/>
</typeAliases>
<!--配置环境-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入带有注解的dao接口所在位置-->
<mappers>
<package name="com.xin.dao"/>
</mappers>
</configuration>
log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
单表CRUD操作(代理DAO方式)
多表查询
缓存的配置
SqlMapConfig.xml
<!-- 配置二级缓存 -->
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
在持久层接口使用注解配置二级缓存
/**
*@author xin
*/
@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {}