世界上流行的框架
Spring
play
Hibernate
MyBatis
Struts等
ORM
ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法。
MyBatis
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
MyBatis 是一个半自动化的ORM框架
持久化
持久化
-
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
- 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
- JDBC就是一种持久化机制。文件IO也是一种持久化机制。
- 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。
-
为什么需要持久化服务呢?那是由于内存本身的缺陷引起的
- 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
- 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。
瞬时状态
事务
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
延迟加载
-
概念:MyBatis中的延迟加载也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫做按需查询。例如,在进行一对多查询的时候,只查询出一方,当程序中需要多方数据时,MyBatis在发出sql语句进行查询,这样子延迟加载就可以减少数据库压力,MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。
-
MyBatis中延迟加载的条件:resultMap可以实现高级映射,例:使用association、collection实现一对一及一对多映射,association、collection具备延迟加载功能。
-
延迟加载的好处:先从单表查询,需要时再从关联表查询,大大提高数据库性能,因为查询单表比关联查询多张表速度要快。
-
使用场景:在对应的四种关系表中,一对多、多对多通常情况下采用延迟加载,多对一、一对一、通常采用立即加载。
-
延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询,因为多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询,会一次性将多张表的所有信息查询出来。
MyBatis环境搭建
导入依赖
<dependencies> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency> </dependencies>
全局配置文件
<?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="jdbc.properties"/>
<!-- 给映射起别名-->
<typeAliases>
<!-- 给某一个类做别名-->
<!-- <typeAlias type="com.csi.smbms.domain.User" alias="User"></typeAlias>-->
<!-- 扫描包,将包中所有类全部自动化命名,规则:首字母大写-->
<package name="com.csi.smbms.domain"/>
</typeAliases>
<!--default代表设置默认指向的数据库-->
<environments default="development">
<!--配置环境,不同环境不同的id名字-->
<!--development开发环境环境-->
<environment id="development">
<!--采用JDBC方式对数据库事务进行commit/rollback-->
<transactionManager type="JDBC"></transactionManager>
<!--采用连接池方式管理数据库-->
<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="test">
<!--采用JDBC方式对数据库事务进行commit/rollback-->
<transactionManager type="JDBC"></transactionManager>
<!--采用连接池方式管理数据库-->
<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>
<!--例如-->
<mappers>
<mapper resource="com/csi/smbms/domain/UserMapper.xml"/>
<mapper resource="com/csi/smbms/domain/ProviderMapper.xml"/>
<mapper resource="com/csi/smbms/domain/BillMapper.xml"/>
</mappers>
</configuration>
属性配置文件
driver = com.mysql.cj.jdbc.Driverurl = jdbc : mysql : //192.168.110.249 : 3306/smbmsusername = rootpassword = root
映射文件
<?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代表命名空间,在mybatis的作用域内,必须保持唯一性,一般都是包名加类名
select:
id:代表方法的名称
resultType:返回值类型
节点中间的文本内容就是一条SQL语句
-->
<!--这里所对应的位置必须是接口的全类名
接口中的方法必须是当前的id名称
方法返回值必须一致
#{}:传统占位符?
${}:statement的拼接,字符串拼接,可能会出现SQL注入问题
-->
<mapper namespace="com.csi.smbms.dao.UserMapper">
<!--这个id是dao里的方法名 返回类型-->
<!-- 用户登录功能 -->
<select id="login" resultType="com.csi.smbms.domain.User" parameterType="String">
select * from smbms_user where userCode = #{arg0} and userPassword=#{arg1}
</select>
<select id="list" resultType="com.csi.smbms.domain.User">
select * from smbms_user
</select>
<select id="findByLikeUserName" resultType="com.csi.smbms.domain.User">
select * from smbms_user where userName LIKE CONCAT('%',#{userName},'%')
</select>
<select id="findByLikeCreationDate" resultType="com.csi.smbms.domain.User">
select * from smbms_user where creationDate LIKE CONCAT('%',#{creationDate},'%')
</select>
<insert id="save" parameterType="com.csi.smbms.domain.User">
insert into smbms_user(userCode,userName,userPassword,gender,birthday,phone,address,userRole,createdBy,creationDate)
values (#{userCode},#{userName},#{userPassword},#{gender},#{birthday},#{phone},#{address},#{userRole},#{createdBy},now());
</insert>
<update id="update" parameterType="com.csi.smbms.domain.User">
update smbms_user set userName = #{userName} ,userCode = #{userCode}, userPassword=#{userPassword} ,gender=#{gender} , birthday=#{birthday}
,phone=#{phone},address=#{address},modifyBy=#{modifyBy}, modifyDate=now()
where id=#{id}
</update>
<delete id="delete" >
delete from smbms_user where id = ${id};
</delete>
<select id="findByConditions" parameterType="User">
select * from smbms_user where userCode = #{userCode} and userRole = #{userRole}
</select>
<!-- select * from smbms_user where userName LIKE CONCAT('%',#{userName},'%') $和#的区别面试题-->
<select id="findByLike" resultType="User" parameterType="String">
select * from smbms_user where userName LIKE '%${userName}%'
</select>
<select id="findById" resultType="com.csi.smbms.domain.User" parameterType="long">
select * from smbms_user where id = #{id}
</select>
<select id="findByCount" resultType="int">
select count(*) from smbms_user
</select>
</mapper>
Mapper接口形式操作
接口名称需要与映射配置文件名称相同映射配置文件中 namespace 必须是接口的全名。接口中的方法名和映射配置文件中的标签的 id 一致。接口中的返回值类型和映射配置文件中的 resultType 的指定的类型一致。声明一个接口,将接口与对应的映射文件放置在同一个文件夹中, namespace 配置需要引用接口名称,接口中的方法名称与映射文件中的节点 id 一致,参数类型、返回值类型一致,在调用时,使用 SqlSession 对象的 getMapper(Mapper.class) 的形式调用。Mapper 接口形式的原理
Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,就是映射文件中 Mapper 的Statement的 id 值;接口方法内的参数,就是传递给 sql 的参数。 Mapper 接口是没有实现类的,当调用接口方法时,接口全限名 + 方法名拼接字符串作为 key 值,可唯一定位一个MapperStatement。
映射类型
日志配置文件
log4j.rootLogger=DEBUG,CONSOLE,file #log4j.rootLogger=ERROR,ROLLING_FILE log4j.logger.com.csi.dao=debug log4j.logger.com.ibatis=debug log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug log4j.logger.java.sql.Connection=debug log4j.logger.java.sql.Statement=debug log4j.logger.java.sql.PreparedStatement=debug log4j.logger.java.sql.ResultSet=debug log4j.logger.org.tuckey.web.filters.urlrewrite.UrlRewriteFilter=debug ###################################################################################### # Console Appender \u65e5\u5fd7\u5728\u63a7\u5236\u8f93\u51fa\u914d\u7f6e ###################################################################################### log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.Threshold=error log4j.appender.CONSOLE.Target=System.out log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern= [%p] %d %c - %m%n ###################################################################################### # DailyRolling File \u6bcf\u5929\u4ea7\u751f\u4e00\u4e2a\u65e5\u5fd7\u6587\u4ef6\uff0c\u6587\u4ef6\u540d\u683c\u5f0f:log2009-09-11 ###################################################################################### log4j.appender.file=org.apache.log4j.DailyRollingFileAppender log4j.appender.file.DatePattern=yyyy-MM-dd log4j.appender.file.File=log.log log4j.appender.file.Append=true log4j.appender.file.Threshold=error log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n
MyBatis的生命周期
用过即丢,其生命周期只存在于方法体内可重用其来创建多个 SqlSessionFactory 实例负责构建 SqlSessionFactory ,并提供多个 build 方法的重载
在创建 SqlSession 的同时,能够将事务设置为手动的或者是自动的作用域: Application生命周期与应用的生命周期相同单例存在于整个应用运行时,并且同时只存在一个对象实例
包含了执行 SQL 所需的所有方法对应一次数据库会话,会话结束必须关闭线程级别,不能共享
MyBatisUtils
package com.csi.smbms.utils;
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 MyBatisUtils {
private static SqlSessionFactory factory;
private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
static {
System.out.println("工具类初始化");
//步骤1:读取配置文件,读,用字节流
InputStream is = null;
try {
is = Resources.getResourceAsStream("mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
//步骤2:通过API对应的功能实现数据库连接 后面的是多数据库切换括号里+“”
factory = new SqlSessionFactoryBuilder().build(is);
}
/**
* SqlSession创建问题
* @return
*/
public static SqlSession getSqlSession(){
//先查一下本地有没有
SqlSession sqlSession = tl.get();
//判断本地线程中是否存在sqlsession 不存在则构造一个新的sqlsession 同时放在ThreadLocal中
if (sqlSession==null){
System.out.println("创建");
sqlSession = factory.openSession();
//没有存放过
tl.set(sqlSession);
}
//不为空
return sqlSession;
}
/**
* 关闭
*/
public static void close(){
SqlSession sqlSession = tl.get();
if (sqlSession!=null){
System.out.println("关闭");
sqlSession.close();
//不要把ThreadLocal整个对象全关了,将其置空
tl.set(null);
}
}
}
操作
查询
List: SelectList(namespace+id, 参数内容 )
SelectOne: 查询一个, SelectOne(namespace+id, 参数内容 ) ;
模糊查询
使用字符串拼接形式 '%${ 变量名称 }%'
使用 concat 方式 concat('%',#{ 变量名称 },'%')
<select id="findByLikeCreationDate" resultType="com.csi.smbms.domain.User">
select * from smbms_user where creationDate LIKE CONCAT('%',#{creationDate},'%')
</select>
测试类
@Test
public void testBillConcatSelect(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BillMapper billMapper = sqlSession.getMapper(BillMapper.class);
Bill bill = billMapper.selectIsPayment("大豆油", 2);
System.out.println(bill);
MyBatisUtils.close();
}
map方式
xml
当查询的条件都是来自于同一张表,在前端,通过类似于 Spring MVC 的框架,能够自动实现数据的封装,此时利用对象封装参数查询就比较方便。当查询的条件都是来自于不同的表内容,那么此时,要么新建立一个临时对象用于作为查询的参数,最好的形式就是 map 来实现该功能。
$与#之间的区别(**)
#{}:传统占位符?,可以有效的防止SQL注入,提高系统安全性。 ${}:statement的拼接,字符串拼接,可能会出现SQL注入问题
#{}是预编译处理,${}是字符串替换。