持久层:可以立即保存在磁盘上,在这里可以理解为与数据库相关操作。
持久层技术解决方案有几种?
1.JDBC技术–>Connection、PreparedStatement、ResultSet
2.Spring的JdbcTemplate–>Spring中对Jdbc的简单封装
3.Apache(阿帕奇)的DBUtils–>它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装
以上这些都不是框架(JDBC是规范、Spring的JdbcTemplate和Apache的DBUtils都只是工具类)
mybatis是一个用Java编写的持久层框架,它使用ORM实现了结果集的封装。
需要拥有两个配置文件,全局配置文件 和 映射文件,在编写这两个映射文件之前,必须创建mybatis环境(引入jar包或者maven工程导入坐标)
ORM是Object Relational Mapping 对象关系映射。简单来说,就是把数据库表和实体类及实体类的属性对应起来,让开发者操作实体类就实现操作数据库表。
它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等烦杂过程
原始 JDBC 的操作问题分析
1. 频繁创建和销毁数据库的连接会造成系统资源浪费从而影响系统性能。
2. sql语句在代码中硬编码,如果要修改 sql 语句,就需要修改 java 代码,造成代码不易维护。
3. 查询操作时,需要手动将结果集中的数据封装到实体对象中。
4. 增删改查操作需要参数时,需要手动将实体对象的数据设置到 sql 语句的占位符。
原始 JDBC 的操作问题解决方案
1. 使用数据库连接池初始化连接资源。
2. 将 sql 语句抽取到配置文件中。
3. 使用反射、内省等底层技术,将实体与表进行属性与字段的自动映射
反射(反射机制)
java的反射指的是类,对象,方法,属性的调用
比如我创建一个对象,能够动态的去获取这个对象下的属性方法(类同理),简单来说,能够 " . "出来,就是反射
映射(映射关系)
java的映射是指像在mybatis中指定包名,类名,是一种关系,通过他的全限定类名去对应指定的包或是类,简单来说,就是通过名字去找指定的人,只是一种联系
内省 (Introspector)是Java语言对JavaBean类的属性,事件和方法的默认处理方式 例如: 类User中有属性name,那么必定有getName,setName方法,内省就是通过这两个方法来获取或者设置name属性的值。
插件 mybatisx
lombok
①添加MyBatis的maven包
②创建user数据表
③编写User实体类
④编写核心文件MyBatis-Config.xml
⑤编写映射文件UserMapper.xml 和 UserMapper接口
⑥编写测试类
junit
junit
4.12
mysql
mysql-connector-java
8.0.31
commons-dbcp
commons-dbcp
1.4
org.mybatis
mybatis
3.5.2
org.projectlombok
lombok
1.18.24
log4j
log4j
1.2.17
org.apache.maven.plugins
maven-surefire-plugin
2.5
true
CREATE TABLE user
(
id
int(11) NOT NULL AUTO_INCREMENT,
username
varchar(255) DEFAULT NULL,
password
varchar(255) DEFAULT NULL,
age
int(11) DEFAULT NULL,
phone
varchar(255) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 等方法,注解后在编译时会自动加进去
@Build即可加到类上,也可以加到方法上。将其加到类上,相当于包含类所有属性的私有构造方法,且构造方法上加上 @Builder 注解。
User.builder()
.age(10)
.id(1)
.phone(“1212121”)
.build();
@Build加到类上,先实例化内部类对象并返回,然后为调用内部类的方法为内部类的属性赋值,build()方法就是将内部类的属性值传入类构造器中,实例化一个类对象,避免一个类出现过多构造函数。
@Data和@Builder一起使用时,反编译的文件中不会有无参构造方法,故在使用时要加上@AllArgsConstructor、@NoArgsConstructor这两个注解。
注解在类上;为类提供一个无参的构造方法
注解在类上;为类提供一个全参的构造方法
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
private int id;
private String username;
private String password;
private String phone;
private int age;
set get 方法
}
可以参考官方文档
https://mybatis.org/mybatis-3/zh/getting-started.html
# 数据库信息 db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/xinzhi666?useUnicode=true&characterEncoding=utf-8
username=root
password=root
# 配置文件 mybaits-config.xml
# 日志文件 log4j.properties
# Global logging configuration
# ERROR WARN INFO DEBUG
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
public interface UserMapper {
List<User> selectAll();
User selectById(int id);
}
//映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--MyBatis的DTD约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
mapper:核心根标签
namespace属性:名称空间
-->
<mapper namespace="com.xinzhi.mapper.UserMapper">
<!--
select:查询功能的标签
id属性:唯一标识
resultType属性:指定结果映射对象类型
parameterType属性:指定参数映射对象类型
-->
<select id="selectAll" resultType="user">
SELECT * FROM user
</select>
<select id="selectById" resultType="com.xinzhi.model.User" parameterType="int">
select * from user where id=#{id}
</select>
</mapper>
mapper.xml文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
mapper>
@Test
public void main1( ) throws IOException {
//加载核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获得sqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//获得sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行sql语句
List<User> users = sqlSession.selectList("com.xinzhi.mapper.UserMapper.selectAll");
//打印结果
System.out.println(users);
User user = sqlSession.selectOne("com.xinzhi.mapper.UserMapper.selectById", 1);
System.out.println(user);
//打印结果
sqlSession.close();
}
# 事务管理器(transactionManager)
- JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
- MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。
# 数据源(dataSource)
- UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
- POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
- JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象。
Mapper 接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的全限定名相同
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同(id名)
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同(参数)
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
@Test
public void main1( ) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取到mapper的代理对象,代理对象直接操作数据库
}
会话,在应用程序中连接数据库要执行连接,然后会关闭,这算一次会话。
一个session可以启动多个事务,session指一次连接。一个session中可以完成多个事务。
一个事务 是指一个操作单元,要么成功,要么失败,没有中间状态。
事务呢,就好比在打开会话后要执行程序中的某一个或多个对数据库进行的操作。
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>aopalliancegroupId>
<artifactId>aopallianceartifactId>
<version>1.0version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.7.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.31version>
dependency>
<dependency>
<groupId>commons-dbcpgroupId>
<artifactId>commons-dbcpartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.5version>
<configuration>
<skipTests>trueskipTests>
configuration>
plugin>
plugins>
build>
注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、toString 等方法,注解后在编译时会自动加进去
@Build即可加到类上,也可以加到方法上。将其加到类上,相当于包含类所有属性的私有构造方法,且构造方法上加上 @Builder 注解。
@Build加到类上,先实例化内部类对象并返回,然后为调用内部类的方法为内部类的属性赋值,build()方法就是将内部类的属性值传入类构造器中,实例化一个类对象,避免一个类出现过多构造函数。
@Data和@Builder一起使用时,反编译的文件中不会有无参构造方法,故在使用时要加上@AllArgsConstructor、@NoArgsConstructor这两个注解。
注解在类上;为类提供一个无参的构造方法
注解在类上;为类提供一个全参的构造方法
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Account {
private int id;
private String name;
private double money;
}
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`money` decimal(11,0) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
public interface AccountMapper {
int update(@Param("name") String name,@Param("money") double money);
//注解: @Param 与映射文件中的参数相匹配 多个参数时得写 单个参数就无所谓
double queryMoney(String name);
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinzhi.dao.AccountMapper">
<update id="update">
update student set money=#{money} where name=#{name}
update>
<select id="queryMoney" resultType="java.lang.Double">
select money from student where name=#{name}
select>
mapper>
说明:
面试题:
mybatis中# 和 $的区别
默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。
这样做更安全,更迅速,通常也是首选做法
public interface IAccountService {
/**
* 转账功能
* @param from 主动转账的人
* @param to 收款的人
* @param money 金额
*/
void transfer(String from, String to, double money);
}
@Service
public class AccountServiceImpl implements IAccountService {
@Autowired
private AccountMapper accountMapper;
public void transfer(String from, String to, double money) {
// 先查询from账户的钱
double fromMoney = accountMapper.queryMoneyByName(from);
// 对from账户进行扣钱操作
accountMapper.updateUser(from, fromMoney-money);
//手动制造异常
// System.out.println(1 / 0);
// 先查询to账户的钱
double toMoney = accountMapper.queryMoneyByName(to);
// 对to账户进行加钱操作
accountMapper.updateUser(to, toMoney+money);
System.out.println("支付宝到账:"+money+"元");
}
}
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xinzhi666?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xinzhi.dao"/>
bean>
<context:component-scan base-package="com.xinzhi"/>
beans>
classpath指 文件夹resources
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name = "cacheEnabled" value = "true" />
settings>
<typeAliases>
<package name="com.xinzhi.model" />
typeAliases>
<mappers>
mappers>
configuration>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AppTest {
@Autowired
private IAccountService accountService;
@Test
public void testTransfer() throws Exception {
accountService.transfer("husband", "wife", 100);
}
}
说明:
其实整合的目的就是把mapper对象放入到容器中。(从接口生成对象,底层还是反射)
注意配置文件的加载,测试类中指定了spring的配置文件, spring的配置文件中又指定了mybatis配置文件和mapper映射文件。
mybatis的缓存主要是针对查询操作的
二级缓存是 mapper 映射级别的缓存, 默认是不开启的,需要手动开启二级缓存
当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,也就是序列化
<setting name="cacheEnabled" value="true"/>settings>
1.读未提交(Read uncommitted):脏读
脏读: 一个事务在执行的过程中读取到了其他事务还没有提交的数据
Tom的账户money=0,公司发工资把5000元打到Tom的账户上,Tom的money=money+5000元,但是该事务并未提交,而Tom正好去查看账户,发现工资已经到账,账户money=5000元,非常高兴,可是不幸的是,公司发现发给Tom的工资金额不对,应该是2000元,于是迅速回滚了事务,修改金额后,将事务提交,Tom再次查看账户时发现账户money=2000元,Tom空欢喜一场,从此郁郁寡欢,走上了不归路……
2.读已提交(Read committed):
可避免 脏读 的发生。
两次读取的数据不一样,自己事务没有提交的时候可以读取别的已经提交的事务。
Tom拿着工资卡去消费,酒足饭饱后在收银台买单,服务员告诉他本次消费1000元,Tom将银行卡给服务员,服务员将银行卡插入POS机,POS机读到卡里余额为3000元,就在Tom磨磨蹭蹭输入密码时,他老婆以迅雷不及掩耳盗铃之势把Tom工资卡的3000元转到自己账户并提交了事务,当Tom输完密码并点击“确认”按钮后,POS机检查到Tom的工资卡已经没有钱,扣款失败,Tom十分纳闷,明明卡里有钱,于是怀疑POS有鬼,和收银小姐姐大打出手,300回合之后终因伤势过重而与世长辞,Tom老婆痛不欲生,郁郁寡欢,从此走上了不归路…
3.可重复读(Repeatable read):幻读
MySql默认隔离级别。自己事务没有提交的时候,读不到别的已经提交的事务。
Tom的老婆工作在银行部门,她时常通过银行内部系统查看Tom的工资卡消费记录。2019年5月的某一天,她查询到Tom当月工资卡的总消费额为80元,Tom的老婆非常吃惊,心想“老公真是太节俭了,嫁给他真好!”,而Tom此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录并提交了事务,沉浸在幸福中的老婆查询了Tom当月工资卡消费明细一探究竟,可查出的结果竟然发现有一笔1000元的消费,Tom的老婆瞬间怒气冲天,外卖订购了一个大号的榴莲,傍晚降临,Tom生活在了水深火热之中,只感到膝盖针扎的痛…
4.串行化(Serializable ):
可避免 脏读、不可重复读、幻读 的发生。
事务有以下特性(ACID):
1、原子性(Atomicity)
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
2、一致性(Consistency)
事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
3、隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
4、持久性(Durability)
已被提交的事务对数据库的修改应该永久保存在数据库中
回顾
1 ORM 封装了什么
2 mybatis的使用步骤
3 lombok
4 事务
5 缓存
6 隔离级别
pom.xml
org.springframework
spring-beans
5.0.7.RELEASE
org.springframework
spring-core
5.0.7.RELEASE
org.springframework
spring-context
5.0.7.RELEASE
org.springframework
spring-expression
5.0.7.RELEASE
org.springframework
spring-aspects
5.0.7.RELEASE
aopalliance
aopalliance
1.0
org.springframework
spring-tx
5.0.7.RELEASE
org.springframework
spring-jdbc
5.0.7.RELEASE
org.springframework
spring-test
5.0.7.RELEASE
junit
junit
4.12
mysql
mysql-connector-java
8.0.31
commons-dbcp
commons-dbcp
1.4
org.mybatis
mybatis
3.5.2
org.mybatis
mybatis-spring
1.3.1
org.projectlombok
lombok
1.18.24
org.apache.maven.plugins
maven-surefire-plugin
2.5
true
applocaltion.xml
db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/students?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
mybatis-config.xml
Mapper.xml