文章目录
框架
1、框架的概念:
- 是一组可重用的组件(工具类)按照一定的规则组成的半成品。
2、框架的好处:
- 提高开发效率;
- 降低开发成本;
- 提高代码可扩展性和可维护性。
3、常见框架组合:
- SSM:Spring + SpringMVC + MyBatis
- SSH:struts2 + spring + hibernate
4、三层架构:
- 表现层:用于展示数据的。
- 业务层:是处理业务需求。
- 持久层:是和数据库交互的。
5、分层的重要性:
- 为了实现软件工程中的“高内聚、低耦合”;将问题划分来各个解决,易于控制、易于延展、意义分配资源。
持久层技术解决方案
1、JDBC技术:
- Connection
- PreparedStatement
- ResultSet
2、Spring的JdbcTemplate:
- spring中对jdbc的简单封装
3、Apache的DBUtils:
- 和SpringTemplate很像,也是对jdbc的简单封装。
以上解决方案都不是框架:
- jdbc是规范;
- Spring的JdbcTemplate和Apache的DBUtils都只是工具类。
为什么不选择jdbc
1、jdbc程序的回顾:
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","ro
ot", "root");
//定义 sql 语句 ?表示占位符
String sql = "select * from user where username = ?";
//获取预处理 statement
preparedStatement = connection.prepareStatement(sql);
//设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
//向数据库发出 sql 执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历查询结果集
while(resultSet.next()){
System.out.println(resultSet.getString("id")+"
"+resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//释放资源
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
上边使用 jdbc 的原始方法(未经封装)实现了查询数据库表记录的操作。
2、jdbc问题分析:
(1)数据库连接创建、释放频繁,造成系统资源浪费从而影响系统性能(使用数据库连接池可以解决此问题);
(2)SQL语句在代码中,(SQL变化可能较大,SQL变动需要改变Java代码)造成代码不易维护;
(3)使用preparedStatement向占位符传参数存在硬编码,(因为SQL语句的where条件是不一定的,修改SQL还需要修改Java代码)系统不易维护。
(4)结果的解析存在硬编码,(SQL变化导致Java代码变化)系统不易维护,(将数据库记录封装到pojo对象解析比较方便)。
MyBatis框架
MyBatis的环境搭建
1、创建maven工程并导入坐标;
2、创建实体类和持久层(dao)接口;
3、创建MyBatis的主配置文件:SqlMapConfig.xml
4、创建映射配置文件:IUserDao.xml
MyBatis环境搭建注意事项
1、搭建的时候,dao的xml、Java类、mapper的起名需要一致;
2、在idea中创建目录和创建包不一样,包是三级结构,目录是一级目录;
3、mybatis的映射配置文件位置必须和dap接口的包结构相同;
4、映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名;
5、映射配置文件的操作配置(select、update、add、delete),ID属性的取值必须是dao接口的方法名。
(遵从了第3、4、5点之后,我们在开发中就无须再写dao的实现类)
MyBatis的设计模式
- MyBatis中的设计模式:工厂模式(SQLSessionFactory)、代理模式(MapperProxyFactory)、构建者模式(SQLSessionFactoryBuilder)。
- Mybatis的执行流程:
(1)读取配置文件:
(2)创建SQLSessionFactory工厂;
(3)使用工厂生产SQLSession对象;
(4)使用SQLSession创建dao接口的代理对象;
(5)使用代理对象执行方法;
(6)释放资源。
(注意事项:不要忘记在映射配置中告知mybatis要封装到哪个实体类中。配置方式:指定实体类的权限定类名。) - 构建者模式:
(1)创建SQLSessionFactory工厂使用了创建者模式。
(2)构建者:就像盖房子,找的包工队。
(3)优点:把对象的创建细节隐藏,让使用者直接调用方法即可拿到对象;减少繁琐的操作。 - 工厂模式:
(1)生产SQLSession使用了工厂模式。
(2)优点:使用工厂去生产一个session对象,省去了new的步骤,降低类之间的依赖关系。 - 代理模式:
(1)创建dao接口实现类使用了代理模式。
(2)优点:在不修改源码的基础上对已有方法增强。
MyBatis实现解析
基于代理Dao实现CRUD操作
- 使用要求:
(1)持久层接口和持久层接口的映射配置必须在相同的包下。
(2)持久层映射配置中mapper标签的namespace属性取值必须是持久层接口的全限定类名。
(3)SQL语句的配置标签,,的id属性必须和持久层接口的方法名相同。 - 细节:
(1)resultType属性:用于指定结果集的类型。
(2)parameterType属性:用于指定传入参数的类型。
(3)SQL语句中使用#{}字符:代表占位符,相当于使用jdbc时的?,都是用于执行语句时替换实际的数据。具体的数据是由#{}里面的内容决定的。 - mybatis中两个占位符的区别:
(1)#:(常用)
①、能够很大程度防止SQL注入。
②、会将传入的参数在解析的时候,加上双引号,当做字符串来解析。
(2)$:
①、不能防止SQL注入。
②、传入的数据直接显示在生成的SQL中。
③、一般用入传入数据库对象,比如数据库表名。
MyBatis的参数深入
- SQL语句进行传参的时候,使用标签的ParameterType属性。
- 该属性的取值可以是基本类型、引用类型(例如:String类型),还可以是实体类类型(POJO类)。还可以使用实体类的包装类。
- 基本类型和引用类型(String类型)可以直接写类型名称,也可以使用包名.类名的方式。例如:java.lang.String。
- 注意:参数只能有一个,如果需要传递多个时,把多个参数封装为一个对象即可。
MyBatis的输出结果封装
1、ResultType:
- 可以指定结果集的类型;支持基本类型和实体类类型。
- 需注意:实体类的属性名称和查询语句中的列名必须保持一致,否则无法实现封装。
- MySQL在Windows系统中不区分大小写(MySQL语句)。
2、ResultMap:
- resultMap标签可以建立查询的列名和实体类的属性名称不一致时建立对应关系;从而实现封装。(type属性:指定实体类的全限定类名;id属性:给定一个唯一标识,给查询select标签引用)。
- id标签:用于指定主键字段。
- result标签:用于指定非主键字段。
- column属性:用于指定数据库列名。
- property属性:用于指定实体类属性名称。
SqlMapConfig.xml配置文件
1、SqlMapConfig.xml中的配置内容和顺序:
2、properties属性:
- 作用:可以简化SqlMapConfig.xml的dataSource标签中的property,以便与该内容可以复用,或者使用配置文件得到。
- 实现方式(有两种):
(1)properties标签定义关联参数:
①、在SqlMapConfig.xml的environments标签上边定义properties标签。
<properties>
<property name="driver" value="com.mysql.jdbc.Driver">
<property name="url" value="jdbc:mysql:///mybatis_day01"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>-->
</properties>
②、简化dataSource的代码。
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
(2)使用db.properties配置文件定义关联参数。
①、导入db.properties配置文件。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_day01
jdbc.username=root
jdbc.password=1234
②、在SqlMapConfig.xml的environments标签上边定义properties标签。
<properties resource="db.properties">
</properties>
③、简化dataSource的代码。
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
3、typeAliases标签(类型别名):
- 作用:简化mapper.xml映射文件中的resultType的类型。
- 使用:
(1)在SqlMapConfig.xml中,定义typeAliases标签。
<typeAliases>
<typeAlias alias="user" type="com.test.domain.User"/>
</typeAliases>
①、typeAliases:用于配置别名。
②、type属性:实体类全限定类名。
③、alias属性:指定别名。
④、当指定了别名,就不再区分大小写。
⑤、配置别名之后,只要是用到(type=“com.test.domain.User”)type属性值的,都可以替换成别名(user)。
⑥、也可以使用下面的package属性。
<typeAliases>
<package name="com.test.domain"/>
</typeAliases>
⑦、package属性:用于指定要配置的别名的包。当指定之后,该包下的实体类都会注册别名,并且类名就是别名。不区分大小写。
4、mappers(映射器):
- 作用:
关联mybatis项目中映射信息,加载mapper.xml映射文件. 以便于程序在运行过程中,找到配置文件,找到sql语句。 - 使用:
(1)在SqlMapConfig.xml中,定义mappers标签。
<mappers>
<mapper resource="com/test/dao/IUserDao.xml"/>
<mapper class="com.test.dao.IUserDao"/>
<package name="com.test.dao"/>
</mappers>
(2) 解析:
①、<mapper resource="com/test/dao/IUserDao.xml"/>
当Dao接口指定了xml映射文件时,使用该标签,加载"Dao接口"关联的mapper.xml映射文件。
resource属性必须从resource目录开始。
②、 <mapper class="com.test.dao.IUserDao"/>
当Dao接口使用了注解配置映射信息时,使用该标签,指定"Dao接口",以便于获取接口上的注解的配置信息
③、<package name="com.test.dao"/>
指定接口所在的包,则这个时候,无论接口使用的注解或者配置文件,都会自动加载.
注意: 如果Dao接口使用的是配置文件的方式,则要求
A、"配置文件所在路径"和"接口所在路径"必须一模一样
B、"配置文件的名字"和"接口的名字"必须一模一样
④、加载同一个接口映射时,<mapper resource=""/>,<mapper class=""/>,<package name==""/>只能有一个.
MyBatis连接池技术
1、连接池:
- 连接池就是用于存储连接的一个容器。
- 容器其实就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一连接。
- 该集合还必须实现队列的特性:先进先出。
- 在开发中都会使用到连接池。
- 好处:可以减少获取连接所消耗的时间。
2、mybatis中的连接池:
(mybatis连接池提供了3种方式的配置)
-
配置的位置:
主配置文件SqlMapConfig.xml中的DataSource标签,type属性就是表示采用何种连接池方式。 -
type属性的取值:
①、POOLED : 使用连接池。
②、UNPOOLED : 不使用连接池。
③、JNDI : 由Tomcat提供连接池。
MyBatis事务控制
1、事务管理:
-
事务的概念:事务,指的是逻辑上的一组操作,组成这组操作的各个逻辑单元要么一起成功,要么一起失败。
-
事务的四大特性:ACID
(1)A:原子性(Atomicity):一是不可分割的最小操作单位,要么同时成功,要么同时失败。
(2)C:一致性(Consistency):从一个一致性的状态到另一个一致性状态。事务在执行前数据库状态和执行后数据库状态保持一致。(例如:转账前2个人的总金额是2000,转账后的总金额还是2000)。
(3)I:隔离性(Isolation):事务与事务之间不应该相互影响,执行时保持隔离的状态。
(4)D:持久性(Durability):一旦事务执行成功,对数据库的修改是持久的。 -
引发的并发访问的问题:
(1)事务在操作时的理想状态:所有的事务之间保持隔离,互不影响。因为并发操作,多个用户同时访问同一个数据,可能引发并发访问的问题。
(2)可能引发的问题:
①、脏读:一个事务读取到了另一个事务中尚未提交的数据。
②、幻读:一个事务中两次读取到的数据的数量不一致,要求在一个事务多次读取的数据的数量是一致的,这是 insert 或 delete 时引发的问题。
③、不可重复读:一个事务中两次读取的数据内容不一致,要求的是一个事务中多次读取时数据是一致的,这是事务 update 时引发的问题。 -
事务的四种隔离级别:
(1)读未提交(read uncommitted):脏读、幻读、不可重复读,都不能解决。
(2)读已提交(read committed):可避免脏读的发生。
(3)可重复读(repeatable read):可避免脏读、不可重复读的发生。
(4)串行化(serializable):三者都可以避免发生。 -
JDBC事务管理(通过Connection连接对象的方法进行管理):
(1)开启事务:conn.setAutoCommit(false); //true表示自动提交事务(默认), false表示手动提交事务。
(2)提交事务:conn.commit()。
(3)回滚事务:conn.rollback()。
2、mybatis的事务管理:
- 获取SqlSession对象时,传递参数true
sqlSession = factory.openSession(true); //表示自动提交事务
- 获取SqlSession对象时,不传递参数,或者传递false
sqlSession = factory.openSession(); //默认false,手动提交事务
sqlSession = factory.openSession(false);//手动提交事务
MyBatis动态SQL语句(标签)
1、if标签:
- test属性:写的是对象的属性名。
- 如果test成立时,就把if标签中的内容,拼接到上面的SQL语句的后面。
2、where标签:
- 用于“多条件不确定”查询时。
3、foreach标签:
- 当需要遍历”传入参数“,并拼接SQL语句时。
- 案例:
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
<include refid="defaultUser"></include>
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
- 解析:(属性)
(1)collection :表示被遍历的集合。
(2)item :集合中的每一个元素。
(3)separator :拼接的每个元素之间的分割。
(4)open :被拼接的语句的开始。
(5)#{uid} :被循环拼接的东西。(与item属性的值要一致)
(6)close :被拼接的语句的结束。 - 注意:
(1)如果要遍历的是对象中的属性,则collection=“属性名”。
(2)如果要遍历的是传入参数本身(也就是说,传递到参数本身就是一个集合或者数组)
①、如果是list集合,则collection=“list”。
②、如果是数组,则collection=“array”。
③、如果是set集合,则:
A、第1步:在接口的方法上,添加@Param(“set”)。
//List<User> findUserBySet(@Param("set") Set<Integer> ids);
B、第2步:collection=“set”。
(3)传入参数仅限一个。
MyBatis延迟加载和立即加载
1、延迟加载:(适用于多表查询)
(1)概念:
在真正使用数据时才发起查询,不用的时候不查询。按需加载(又称懒加载)。
(2)什么情况下使用:
一对多、多对多的表关系结构中,通常采用延迟加载。
(3)坏处:
因为只有当用到数据时,才会进行数据库查询,这样在大批量数据查询时,(因为查询工作也需要消耗时间),所以可能造成用户等待时间变长,造成用户体验下降。
(4)好处:
先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能。因为查询单表要比关联查询多张表速度要快。
(节省内存,提高程序运行效率)。
2、立即加载:
(1)概念:
不管用不用,只要一调用方法,马上发起查询。
(2)什么情况下使用:
一对一、多对一的表结构中,通常采用立即加载。
MyBatis的缓存
1、什么是缓存:
- 存在于内存中的临时数据。
2、为什么使用缓存:
- 较少和数据库的交互次数,提高执行效率。
3、什么样的数据使用缓存,什么样的数据不能使用?
(1)适用于缓存:
- 经常查询并且不经常改变的;
- 数据的正确与否与否对最终结果影响不大的。
(2)不适用于缓存:
- 经常改变的数据;
- 数据的正确与否对最终结果影响很大的。
(例如:商品的库存,银行的汇率,股市的牌价)
4、MyBatis的一级缓存:(默认开启)
- 是SqlSession级别的缓存。只要SqlSession不销毁,缓存就一直存在。
- 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中。
- 该区域的结构是一个map。
- 当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。
- 当SqlSession对象消失时,mybatis的一级缓存也就消失了。
- 如果在SqlSession调用增删改和commit()、close()等方法的时候,缓存会被自动清空。
5、MyBatis的二级缓存:(默认不开启)
- 指的是SQLSessionFactory对象的缓存。由同一个SQLSessionFactory对象创建的SqlSession共享其缓存。(多个SqlSession共享一个缓存)
- 二级缓存的使用步骤:
(1)让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
(2)让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
//<!--开启user支持二级缓存,每个类要单独开启 -->
<cache/>
(3)让当前的操作支持二级缓存(在select标签中配置)
//useCache="true",让当前方法,支持二级缓存,每个方法要单独设置
<select id="findById" parameterType="INT" resultType="user" useCache="true">
select * from user where id = #{uid}
</select>
MyBatis与Jdbc编程的比较
Jdbc的问题描述与mybatis的解决:
1、问题一:据库连接创建、释放频繁,造成系统资源浪费从而影响系统性能。
解决:在SqlMapConfig.xml中配置数据库连接池,使用连接池管理数据库连接。
2、问题二:Sql语句写在代码中造成代码不易维护,实际SQL变化的可能性较大,SQL变动需要改变Java代码。
解决:将Sql语句配置在XXXMapper.xml文件中与Java代码分离。
3、问题三:向SQL语句传参数麻烦,因为SQL语句的where条件不一定,可能多也可能少,占位符需要和参数对应。
解决:mybatis自动将Java对象映射至SQL语句,通过statement中的parameterType定义输入参数的类型。
4、问题四:对结果集解析麻烦,SQL变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
解决:mybatis自动将SQL执行结果映射至Java对象,通过statement中的resultType定义输出结果的类型。