Spring框架是一个分层架构,由七个模块组成
Spring模块构建在核心容器之上
每个模块(或组件)可以单独存在,也可以和其他模块联合使用
1.核心容器
2.Spring上下文
3.Spring AOP
4.Spring DAO
5.Spring ORM
6.Spring Web模块
7.Spring MVC 框架
maven依赖配置
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.18version>
dependency>
applicationContext.xml
<bean id="hello1" class="com.dx.bean.HelloSpring"/>
<bean id="hello" class="com.dx.bean.HelloSpring" primary="true"/>
test.java
/**
* 获取对象,并调用其方法
* */
@Test
public void iocTest(){
//1.初始化ioc容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器中取出对象
//方式1:通过name获取Bean对象,如果有多个此类型的bean对象时,获取标识的主bean,如果没有标识则抛异常
HelloSpring hello = (HelloSpring) ac.getBean("hello");
//方式2:通过bean的类型获取Bean对象
HelloSpring hello1 = ac.getBean(HelloSpring.class);
//方式3:通过name和类型获取Bean对象
HelloSpring hello2 = ac.getBean("hello",HelloSpring.class);
hello.show();
}
singleton: 单例,默认
prototype: 原型(多例)
用法
<bean id="book" class="com.bjpowernode.bean.Book" scope="singleton"/>
<bean id="book" class="com.bjpowernode.bean.Book" scope="prototype"/>
通过构造方法创建Bean对象
为Bean对象中的成员变量赋值
调用Bean对象中初始化方法(init-method)
Bean对象的使用
当容器关闭之前,调用Bean对象的销毁方法(destroy-method)
applicationContext.xml
<bean id="dog" class="com.bjpowernode.bean.Dog" init-method="init" destroy-method="destroy">
<property name="name" value="哈士奇"/>
bean>
Dog.java
public class Dog {
private String name;
public Dog(){
System.out.println("Dog对象被创建...");
}
public void init(){
System.out.println("Dog对象的初始化方法...");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("name属性被赋值...");
this.name = name;
}
public void destroy(){
System.out.println("Dog对象的销毁方法...");
}
}
test.java
@Test
public void lifeCycle() {
System.out.println("=============IOC容器初始化阶段===============");
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("=============对象使用阶段===============");
Dog dog = ac.getBean("dog", Dog.class);
System.out.println(dog);
System.out.println(dog.getName());
System.out.println("=============IOC容器关闭阶段===============");
ac.close();
}
先创建指定的bean,再创建自己
<bean id="emp" class="com.dx.bean.Emp" depends-on="dept"/>
<bean id="dept" class="com.dx.bean.Dept"/>
构造方法
通过反射机制调用类中无参或有参构造方法来是实现Bean的创建
<bean id="cat" class="com.dx.bean2.Cat"/>
public class Cat {
public String name;
@Override
public String toString() {
return name + "猫咪对象";
}
}
测试类(下同)
@Test
public void factory() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Cat cat = ac.getBean("cat", Cat.class);
System.out.println(cat);
}
静态工厂,实例工厂
通过一个工厂类来间接实现对象创建,将Bean对象放置在IOC容器
<bean id="cat1" class="com.dx.factory.CatStaticFactory" factory-method="getInstance"/>
/* Cat对象的静态工厂 */
public class CatStaticFactory {
public static Cat getInstance(){
Cat cat = new Cat();
cat.name = "加菲猫";
return cat;
}
}
<bean id="factory" class="com.dx.factory.CatInstanceFactory"/>
<bean id="cat2" factory-method="getInstance" factory-bean="factory"/>
/* Cat对象的实例工厂 */
public class CatInstanceFactory {
public Cat getInstance(){
Cat cat = new Cat();
cat.name = "黑猫警长";
return cat;
}
}
FactoryBean接口
<bean id="cat3" class="com.dx.factory.CatFactoryBean"/>
/* 实现FactoryBean接口 */
public class CatFactoryBean implements FactoryBean<Cat> {
/*获取对象*/
@Override
public Cat getObject() throws Exception {
Cat cat = new Cat();
cat.name = "小花猫";
return cat;
}
/*获取对象的Class类型*/
@Override
public Class<?> getObjectType() {
return Cat.class;
}
/*配置当前对象是否为单例模式*/
@Override
public boolean isSingleton() {
return true;
}
}
注解
略,在后面
Dependency Injection
依赖:在A类中使用B类:A类对B类产生依赖关系。
public class Classes{}
public class Student{
String sname;
Integer age;
Double score;
Classes classes;
public static void main(String[] args){
Student s1 = new Student();
}
}
可以说Student类对String, Integer, Double, Classes类产生了依赖关系
注入:为依赖类型的变量赋值
所谓控制反转就是:获得依赖对象的方式反转了
构造方法
public class Car {
private Integer id;
private String name;
private String type;
private Double price;
public Car(Integer id, String name, String type, Double price) {
this.id = id;
this.name = name;
this.type = type;
this.price = price;
}
}
<bean id="car" class="com.dx.bean2.Car">
<constructor-arg value="001"/>
<constructor-arg value="奥迪"/>
<constructor-arg value="轿车"/>
<constructor-arg value="49.0"/>
<constructor-arg value="奥迪" index="1"/>
<constructor-arg value="001" index="0"/>
<constructor-arg value="49.0" index="3"/>
<constructor-arg value="轿车" index="2"/>
<constructor-arg value="奥迪" name="name"/>
<constructor-arg value="001" name="id"/>
<constructor-arg value="49.0" name="price"/>
<constructor-arg value="轿车" name="type"/>
bean>
set方法
public class Student {
private String name;
private Integer age;
private Double score;
private Boolean gender;
private Date birthday;
private Address address;
private String[] courseNames;
private List<String> list;
private Set<Integer> set;
private Map<String, Double> map;
private Properties properties;
//set和get方法这里省略了
//......
}
public class Address {
private String city;
private String area;
private String street;
//set...... get......
}
<bean id="student" class="com.dx.bean2.Student">
<property name="name" value="小明"/>
<property name="age" value="22"/>
<property name="score" value="86.5"/>
<property name="gender" value="true"/>
<property name="birthday" value="1998/05/19"/>
<property name="address">
<bean class="com.dx.bean2.Address">
<property name="city" value="北京市"/>
<property name="area" value="海淀区"/>
<property name="street" value="人民路"/>
bean>
property>
<property name="courseNames">
<array>
<value>语文value>
<value>数学value>
<value>外语value>
array>
property>
<property name="list">
<list>
<value>aaavalue>
<value>bbbvalue>
<value>cccvalue>
<value>dddvalue>
list>
property>
<property name="set">
<set>
<value>10value>
<value>20value>
<value>30value>
<value>40value>
set>
property>
<property name="map">
<map>
<entry key="one" value="1.2"/>
<entry key="two" value="2.6"/>
<entry key="three" value="3.14"/>
map>
property>
<property name="properties">
<props>
<prop key="jdbc.driver">com.mysql.jdbc.Driverprop>
<prop key="jdbc.url">jdbc:mysql://localhost:3306/testprop>
<prop key="jdbc.username">rootprop>
<prop key="jdbc.password">123prop>
props>
property>
bean>
<bean id="address" class="com.dx.bean2.Address">
<property name="city" value="郑州市"/>
<property name="area" value="金水区"/>
<property name="street" value="民航路"/>
bean>
注解
略,在后面
P命名空间注入(注入属性: properties)
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<bean id="user" class="com.dx.pojo.User" p:name="dx" p:age="18"/>
C命名空间注入(注入构造方法的参数: Constructor)
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<bean id="user" class="com.dx.pojo.User" c:name="dx" c:age="18"/>
在进行自定类型的数据依赖注入时,IOC容器会自动查找匹配对象,实施依赖注入
条件:仅限于自定义类型的数据(POJO)
实现:autowire属性
类型:
no: 不自动装配
byName: 根据属性名称匹配,如果没有找到Bean,不进行自动装配,不会报错。
byType: 根据属性类型匹配,如果没有找到此类型Bean,不进行自动装配,不会报错。
但是如果找到多个没有标识Primary的Bean,就是报错。
default: 默认值,会根据父标签beans标签中default-autowire属性来取值。
缺点: 不灵活,不能实现即能根据名称匹配又能根据类型匹配
实现
public class Order {
private String orderNum;
private Double price;
private Customer customer;
//get set....
}
public class Customer {
private String username;
private String password;
//get set....
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName">
<bean id="order" class="com.dx.bean2.Order" autowire="byType">
<property name="orderNum" value="XA20220712001"/>
<property name="price" value="3998"/>
bean>
<bean id="cust" class="com.dx.bean2.Customer" primary="true">
<property name="username" value="tom"/>
<property name="password" value="123"/>
bean>
<bean id="customer" class="com.dx.bean2.Customer">
<property name="username" value="jerry"/>
<property name="password" value="321"/>
bean>
beans>
@Test
public void autowire() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Order order = ac.getBean("order", Order.class);
System.out.println(order);
}
先配置命名空间和标签规范。配置在当前项目中哪些包中注解是生效的。
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
配置Bean
依赖注入
使用方法
// @Controller
// @Service
// @Repository
@Component("dd")
// @Primary
public class Department {
@Value("10")
private Integer id;
@Value("研发部")
private String dname;
//get... set...
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component("abc")
public class Employee {
@Value("123")
private Integer id;
@Value("张三")
private String ename;
@Autowired
@Qualifier("dd") //通过注解指定bean
// @Resource
private Department department;
}
<context:component-scan base-package="com.dx"/>
<bean id="department" class="com.dx.bean2.Department">
<property name="id" value="666"/>
<property name="dname" value="李四"/>
bean>
@Test
public void annotation() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//依赖注入简单类型
Department department = ac.getBean("dd", Department.class);
System.out.println(department);
//依赖注入自定义类型
Employee employee = ac.getBean("abc", Employee.class);
System.out.println(employee);
}
实现方法
public interface UserDao {
void insert();
void update();
void delete(int id);
List<String> select();
}
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("UserDaoImpl---->insert...........");
}
@Override
public void update() {
System.out.println("UserDaoImpl---->update...........");
}
@Override
public void delete(int id) {
System.out.println("UserDaoImpl---->delete,id="+id);
}
@Override
public List<String> select() {
System.out.println("UserDaoImpl---->select...........");
return Arrays.asList("111","222","333");
}
}
/**
* 静态代理
* */
public class UserStaticProxy implements UserDao {
private UserDao userDao = null;
public UserStaticProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void insert() {
System.out.println("代理 增加的功能--->insert");
userDao.insert();
}
@Override
public void update() {
System.out.println("代理 增加的功能--->update");
userDao.update();
}
@Override
public void delete(int id) {
System.out.println("代理 增加的功能--->delete id="+id);
userDao.delete(id);
}
@Override
public List<String> select() {
System.out.println("代理 增加的功能--->select");
return userDao.select();
}
}
/**
* 静态代理
* */
@Test
public void StaticProxy(){
UserStaticProxy userDaoProxy = new UserStaticProxy(new UserDaoImpl());
userDaoProxy.insert();
userDaoProxy.update();
userDaoProxy.delete(10);
List<String> list = userDaoProxy.select();
System.out.println(list);
}
动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的
动态代理分为两类
一个动态代理 , 一般代理某一类业务,可以代理多个类,代理的是接口!
JdkProxy 实现方式
public class JdkProxy implements InvocationHandler {
/**
* 执行被代理对象中的方法
* @param proxy 被代理对象
* @param method 目标方法
* @param args 目标方法的参数值
* @return 目标方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是代理类添加的辅助业务。");
//执行目标业务方法,利用反射
Object result = method.invoke(targetClass.newInstance(), args);
return result;
}
private Class targetClass;
/**
* 获取代理对象方法
* 代理类对象:new JdbProxy(), 作用:生产代理对象
* 代理对象:创建代理对象Proxy.newProxyInstance(),JDK代理对象实现了目标类接口的一个种对象
*/
public Object getProxy(Class targetClass) {
this.targetClass =targetClass;
/*
* newProxyInstance(loader,interfaces,this);生成代理对象
* 三个参数分别如下
* ClassLoader loader :业务类的类加载器
* Class>[] interfaces:业务类实现的所有接口
* InvocationHandler h :代理类对象(此类)
*/
//业务类的类加载器
ClassLoader classLoader = targetClass.getClassLoader();
//业务类实现的所有接口
Class[] interfaces = this.targetClass.getInterfaces();
//生成代理对象
Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, this);
return proxyObject;
}
}
CglibProxy 实现方式
/**
* cglib动态代理
* 对目标没有要求,普通类就可以,不用实现接口
* 代理类是目标类的子类(父子继承)
* */
public class CglibProxy implements MethodInterceptor {
/**
* 执行目标方法
* @param o 代理对象
* @param method 目标方法对象
* @param objects 目标方法参数值
* @param methodProxy 代理方法的代理对象
* @return 目标方法的返回值
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("我是代理类添加的辅助业务。");
//执行目标业务方法
Object result = method.invoke(targetClass.newInstance(), objects);
return result;
}
private Class targetClass;
/**
* 获取代理对象方法
*/
public Object getProxy(Class targetClass) {
this.targetClass =targetClass;
Enhancer enhancer = new Enhancer();
//目标对象设置为代理对象的父类
enhancer.setSuperclass(targetClass);
//设置回调,调用intercept方法
enhancer.setCallback(this);
return enhancer.create();
}
}
测试类
/**
* 动态代理
* 两种方式,效果一样
* */
@Test
public void JdkProxy(){
//使用JdkProxy
// UserDao userDaoProxy = (UserDao) new JdkProxy().getProxy(UserDaoImpl.class);
//使用CglibProxy
UserDao userDaoProxy = (UserDao) new CglibProxy().getProxy(UserDaoImpl.class);
userDaoProxy.insert();
userDaoProxy.update();
userDaoProxy.delete(100);
System.out.println(userDaoProxy.select());
}
引入依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.21version>
dependency>
切面类
/**
* 切面类,用来记录日志
* 1.无需实现任何接口
* 2.无需使用反射机制调用目标方法
* 3.无侵入
* */
public class LogAspect {
//JoinPoint对象封装了Aop中切面方法的信息
public void beforeMethod(JoinPoint joinPoint){
System.out.println("我是前置通知日志...");
}
public void afterMethod(JoinPoint joinPoint){
System.out.println("我是后置通知日志...");
}
public void returnMethod(JoinPoint joinPoint,Object result){
System.out.println("我是返回通知日志...返回值:"+result);
}
public void throwMethod(JoinPoint joinPoint,Exception e){
System.out.println("我是异常通知日志...异常为:"+e.getMessage());
}
//环绕通知
public void aroundMethod(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("环绕通知的前置配置");
try {
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕通知的返回结果="+proceed);
} catch (Throwable e) {
System.out.println("环绕通知的异常=" + e.getMessage());
}
System.out.println("环绕通知的后置配置");
}
}
写applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="user" class="com.dx.dao.impl.UserDaoImpl"/>
<bean id="role" class="com.dx.dao.impl.RoleDaoImpl"/>
<bean id="log" class="com.dx.aspect.LogAspect"/>
<aop:config>
<aop:pointcut id="exp" expression="execution(* com.dx.dao.impl.*.*(..))"/>
<aop:aspect ref="log">
<aop:before method="beforeMethod" pointcut="execution(* com.dx.dao.impl.*Impl.*(..))"/>
<aop:after method="afterMethod" pointcut-ref="exp"/>
<aop:after-returning method="returnMethod" returning="result"
pointcut="execution(* com.dx.dao.impl.UserDaoImpl.select(..))"/>
<aop:after-throwing method="throwMethod" throwing="e"
pointcut="execution(* com.dx.dao.impl.UserDaoImpl.delete(..))"/>
<aop:around method="aroundMethod" pointcut-ref="exp"/>
aop:aspect>
aop:config>
beans>
切点表达式
编写规则:以execution()包裹
1.单个方法
语法:execution(方法访问修饰符 方法返回值类型 方法所在包名.方法所在类名.方法名(参数类型))
例如:execution(public void com.bjpowernode.dao.impl.UserDaoImpl.insert())
2.多个方法,通配符*
语法:execution(* *.*.*(..))
第一星不限制方法访问修饰符和返回值类型
第二星不限制方法所在的包
第三星不限制方法所在的类
第四星不限制方法名称
两个点不限制方法的参数
例如:execution(* com.bjpowernode.dao.impl.UserDaoImpl.*(..))
例如:execution(* com.bjpowernode.dao.impl.*Impl.*(..))
例如:execution(* com.bjpowernode.*.service.impl.*Impl.*(..))
配置通知
aop:before前置通知
aop:after后置通知
aop:after-returning返回通知
aop:after-throwing异常通知
属性:
method: 切面对象的方法名称
pointcut: 切点表达式, 将通知应用在那些方法上的一种表达式
pointcut-ref: 切面表达式的引用
returning: 是返回通知中的独有属性,配置接收方法的返回值参数
throwing: 是异常通知中的独有属性,配置接收方法抛出异常对象
切面类
/**
* 切面类
* 使用注解配置
* */
@Aspect
@Order(3) //设置切面执行顺序
public class OtherAspect {
//配置一个公共的切面表达式,供其他注解调用
@Pointcut("execution(* com.dx.dao.impl.*.*(..))")
public void exp(){}
@Before("execution(* com.dx.dao.impl.*.*(..))")
public void before(){
System.out.println("Other的------前置通知...");
}
@After("exp()")
public void after(){
System.out.println("Other的------后置通知日志...");
}
// @AfterReturning
// @AfterThrowing
}
applicationContext.xml
<bean id="other" class="com.dx.aspect.OtherAspect"/>
<aop:aspectj-autoproxy/>
@Order(1)
注解<aop:aspect ref="other" order="2">
<aop:before method="before" pointcut-ref="exp"/>
<aop:after method="after" pointcut-ref="exp"/>
aop:aspect>
导入依赖
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.18version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.21version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.4version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.0version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
dependencies>
配置文件
#mysql
jdbc.mysql.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.mysql.url=jdbc:mysql://localhost:3306/student?serverTimezone=UTC
jdbc.mysql.username=root
jdbc.mysql.password=111111
# 全局日志配置
log4j.rootLogger=DEBUG, stdout
# MyBatis 日志配置
#局部日志记录配置
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %c.%M() --%m%n
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = D:/logs/test.log
#log4j.appender.file.Append = true
#log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
}
dao
public interface EmpDao {
List<Emp> select();
Emp selectById(Integer empno);
int insert(Emp entity);
int update(Emp entity);
int deleteById(Integer empno);
}
EmpMapper.xml
<mapper namespace="com.dx.dao.EmpDao">
<select id="select" resultType="emp">
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp order by empno asc
select>
<select id="selectById" parameterType="int" resultType="Emp">
select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp where empno=#{empno}
select>
<insert id="insert" parameterType="com.dx.entity.Emp">
insert into emp(ename,job,mgr,hiredate,sal,comm,deptno)
values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
insert>
<update id="update" parameterType="com.dx.entity.Emp">
update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm},deptno=#{deptno}
where empno=#{empno}
update>
<delete id="deleteById" parameterType="int">
delete from emp where empno=#{empno}
delete>
mapper>
mybatis-config.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
settings>
<typeAliases>
<package name="com.dx.entity"/>
typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">plugin>
plugins>
configuration>
applicationContext.xml⭐
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.dx"/>
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.mysql.driverClassName}"/>
<property name="url" value="${jdbc.mysql.url}"/>
<property name="username" value="${jdbc.mysql.username}"/>
<property name="password" value="${jdbc.mysql.password}"/>
bean>
<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 id="empDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<property name="mapperInterface" value="com.dx.dao.EmpDao"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.dx.dao"/>
bean>
beans>
EmpController.java
/**
* 替代Serlvet
* Controller web层:接收请求,处理请求,给予响应
*/
@Controller
public class EmpController {
@Autowired
private EmpService empService;
public PageInfo<Emp> page(Integer pageNumber, Integer pageSize){
return empService.page(pageNumber, pageSize);
}
public List<Emp> list(){
return empService.list();
}
public Emp get(Integer empno){
return empService.getById(empno);
}
public boolean save(Emp entity){
return empService.save(entity);
}
public boolean edit(Emp entity){
return empService.edit(entity);
}
public boolean remove(Integer empno){
return empService.remove(empno);
}
}
测试类
@Test
public void pageTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpController empController = ac.getBean(EmpController.class);
PageInfo<Emp> pageInfo = empController.page(2, 5);
List<Emp> list = pageInfo.getList();
for (Emp emp : list) {
System.out.println(emp);
}
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("总记录数:" + pageInfo.getTotal());
}
@Test
public void listTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpController empController = ac.getBean(EmpController.class);
List<Emp> list = empController.list();
for (Emp emp : list) {
System.out.println(emp);
}
}
@Test
public void insertTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpController empController = ac.getBean(EmpController.class);
Emp emp = new Emp(null,"小杨","经理",100,new Date(),6500.0,1000.0,10);
boolean result = empController.save(emp);
System.out.println(result);
}
@Test
public void updateTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpController empController = ac.getBean(EmpController.class);
Emp emp = new Emp(7935,"小陈","经理",100,new Date(),6500.0,1000.0,10);
boolean result = empController.edit(emp);
System.out.println(result);
}
@Test
public void deleteTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
EmpController empController = ac.getBean(EmpController.class);
boolean result = empController.remove(7937);
System.out.println(result);
}
//设置关闭自动提交
connection.setAutoCommit(false);
try{
//执行多个SQL语句
...
connection.commit();
}catch(Exception e){
connection.rollback();
}
利用AOP来管理事务,由spring来实现事务的提交或回滚。
声明式事务:1.配置事务管理器(切面)2.配置事务切入点
JDBC的事务管理器:DataSourceTransactionManager,它类似于一个切面
Hibernate的事务管理类器:HibernateTransactionManager
数据库中自带的特性,在SQL标准中有四种:读未提交、读已提交、可重复读、可序列化。
读未提交:读取其他事务没有提交的数据,读取其他事务的内存。会引起脏读、不可重复读、幻读。
读已提交:仅能读取到其他事务提交的数据。可以避免脏读,但是会发生不可重复读、幻读。
可重复读:在一个事务操作表时,将它操作的列锁定,使其他事务无法进行列的修改。可以避免脏读、不可重复读,但是会发生幻读。
可序列化:在一个事务操作表时,将它操作的整个表锁定,使其他事务无法进行表的修改。可以避免所有的并发问题,但是效率低。
事务并发出现的三种问题:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
private Integer id;
private String name;
private Double balance;
}
public interface AccountDao {
void updateBalance(Account account);
Account selectById(Integer id);
}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dx.dao.AccountDao">
<select id="selectById" parameterType="int" resultType="Account">
select id, name, balance from account where id=#{id}
select>
<update id="updateBalance" parameterType="com.dx.entity.Account">
update account set balance=#{balance} where id=#{id}
update>
mapper>
public interface AccountService {
/*转账*/
void transferMoney(Integer fromId, Integer toId, Double money) throws Exception;
}
@Service
// @Transactional(rollbackFor = Exception.class)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void transferMoney(Integer fromId, Integer toId, Double money) throws Exception {
Account fromAccount = accountDao.selectById(fromId);
Account toAccount = accountDao.selectById(toId);
fromAccount.setBalance(fromAccount.getBalance()-money);
accountDao.updateBalance(fromAccount);
System.out.println("扣款成功");
//模拟异常:Spring默认回滚的是运行时异常,可以在配置文件或注解中配置
// System.out.println(10/0);
Class.forName("aaa");
//事务超时,自动回滚
// Thread.sleep(5000);
toAccount.setBalance(toAccount.getBalance()+money);
accountDao.updateBalance(toAccount);
System.out.println("转账完成");
}
}
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="transactionInterceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transferMoney" propagation="REQUIRES_NEW" isolation="DEFAULT" rollback-for="java.lang.Exception" timeout="4"/>
<tx:method name="query*" rollback-for="java.lang.Exception" read-only="true"/>
<tx:method name="query*" rollback-for="java.lang.Exception" read-only="true"/>
<tx:method name="find*" rollback-for="java.lang.Exception" read-only="true"/>
<tx:method name="get*" rollback-for="java.lang.Exception" read-only="true"/>
<tx:method name="list*" rollback-for="java.lang.Exception" read-only="true"/>
<tx:method name="page*" rollback-for="java.lang.Exception" read-only="true"/>
<tx:method name="add*" rollback-for="java.lang.Exception"/>
<tx:method name="save*" rollback-for="java.lang.Exception"/>
<tx:method name="insert*" rollback-for="java.lang.Exception"/>
<tx:method name="edit*" rollback-for="java.lang.Exception"/>
<tx:method name="update*" rollback-for="java.lang.Exception"/>
<tx:method name="delete*" rollback-for="java.lang.Exception"/>
<tx:method name="del*" rollback-for="java.lang.Exception"/>
<tx:method name="remove*" rollback-for="java.lang.Exception"/>
<tx:method name="*"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="exp" expression="execution(* com.dx.service.impl.*Impl.*(..))"/>
<aop:advisor advice-ref="transactionInterceptor" pointcut-ref="exp"/>
aop:config>
public interface AccountService {
/*多商品一起付款*/
void payOrder(Integer fromId, Map<Integer,Double> map) throws Exception;
}
@Service
@Transactional(rollbackFor = Exception.class)
public class AccountServiceImpl implements AccountService {
//注入自己
@Autowired
private AccountService accountService;
@Override
public void transferMoney(Integer fromId, Integer toId, Double money) throws Exception {
Account fromAccount = accountDao.selectById(fromId);
Account toAccount = accountDao.selectById(toId);
fromAccount.setBalance(fromAccount.getBalance()-money);
accountDao.updateBalance(fromAccount);
System.out.println("扣款成功");
toAccount.setBalance(toAccount.getBalance()+money);
accountDao.updateBalance(toAccount);
System.out.println("转账完成");
}
@Override
//注解式事务
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED,
rollbackFor = Exception.class, rollbackForClassName = "java.lang.Exception",
readOnly = true, timeout = 5)
public void payOrder(Integer fromId, Map<Integer, Double> map) throws Exception {
Set<Integer> toIdSet = map.keySet();
System.out.println(toIdSet.size());
for (Integer toId : toIdSet) {
System.out.println("toId="+toId);
double money = map.get(toId);
accountService.transferMoney(fromId,toId,money);
//模拟异常
String s=null;
s.trim();
}
System.out.println("订单支付成功");
}
}
<tx:annotation-driven/>
mId, Map
}
```java
@Service
@Transactional(rollbackFor = Exception.class)
public class AccountServiceImpl implements AccountService {
//注入自己
@Autowired
private AccountService accountService;
@Override
public void transferMoney(Integer fromId, Integer toId, Double money) throws Exception {
Account fromAccount = accountDao.selectById(fromId);
Account toAccount = accountDao.selectById(toId);
fromAccount.setBalance(fromAccount.getBalance()-money);
accountDao.updateBalance(fromAccount);
System.out.println("扣款成功");
toAccount.setBalance(toAccount.getBalance()+money);
accountDao.updateBalance(toAccount);
System.out.println("转账完成");
}
@Override
//注解式事务
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED,
rollbackFor = Exception.class, rollbackForClassName = "java.lang.Exception",
readOnly = true, timeout = 5)
public void payOrder(Integer fromId, Map map) throws Exception {
Set toIdSet = map.keySet();
System.out.println(toIdSet.size());
for (Integer toId : toIdSet) {
System.out.println("toId="+toId);
double money = map.get(toId);
accountService.transferMoney(fromId,toId,money);
//模拟异常
String s=null;
s.trim();
}
System.out.println("订单支付成功");
}
}
<tx:annotation-driven/>