SSM-Spring
Spring-Ico|DI概念介绍
注意bean包下的对象如果你添加上了有参构造器那就必须加上空参构造器,如果没有加上有参构造器的话,空参构造器添不添加随意,主要是spring创建对象时使用的是空参构造器
//IOC的反转:创建对象这份工作由我们自己执行反转给spring帮我们执行
//IOC的控制:就是由spring帮我们负责创建销毁对象,掌握对象的生命周期等,我们在需要使用对象的时候跟spring申请即可
//IOC是一种编程思想,也是一种新的设计理念
@Test
public void Test2() {
//通过spring配置文件获取容器对象
//applicationContext配置的所有bean都會在容器创建的时候被创建出来(使用空参构造器)
//若配置的bean较多,那么在创建容器的时候,会产生内存过大的问题,此问题在机器硬件落后的时候体现明显
/*
* 延迟加载 为true就是创建容器时不加载配置的bean对象,在获取的时候才创建
*/
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
//User u = (User)ac.getBean("user");
User u=ac.getBean(User.class);//传递字节码类型 不需要强转了
System.out.println(u);
}
bean属性讲解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">
<!-- name是一个名字 我们可以通过name来利用容器获取对象
name可以使用特殊字符 可以重复
我们在实际开发中不推荐将多个对象名字命名为重复的-->
<!--id与name作用基本相同,但不推荐使用 ,id不支持特殊字符 不能重复 -->
<!-- class:被管理对象的全包名,spring会通过这个包名来创建对象 -->
<!-- scope Ⅰ singleton Ⅱ prototype 单例与多例
Ⅲ request 在web环境下,如果scope属性为request那么这个对象被创建出来 他的生命周期会与request请求一致
Ⅳ session 同理 生命周期与session一致 -->
<!-- -->
<bean name="user" class="com.sikiedu.bean.User" scope="singleton" init-method="userInit" destroy-method="userDestory">
<property name="u_id" value="2"></property><!-- 依赖注入 -->
</bean>
</beans>
HelloSpring.java
// 单例 只创建一个对象 一般项目中使用singleton
@Test
public void Test3() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// scope = "singleton" 默认值 单例的
User u1 = ac.getBean(User.class);
User u2 = ac.getBean(User.class);
System.out.println(u1 == u2);// true
}
//多例 在获取的时候创建新的对象 特殊环境下改为prototype 比如项目中使用struts就要使用多例
@Test
public void Test4() {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// scope = "prototype"
User u1 = ac.getBean(User.class);
User u2 = ac.getBean(User.class);
System.out.println(u1 == u2);// false
}
//init-method 与destroy-method
@Test
public void Test5() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// scope = "prototype" 默认值 单例的
User u1 = ac.getBean(User.class);
User u2 = ac.getBean(User.class);
System.out.println(u1 == u2);// false
//如果设置scope = "prototype" 那么就不会触发销毁函数
//想要触发销毁函数 需设置scope = "singleton"
//关闭容器 触发bean的destroy-method
ac.close();
}
属性注入
Pet.java
package com.sikiedu.bean;
public class Pet {
//宠物类型 猫 狗
private String petType;
private String color;
public String getPetType() {
return petType;
}
public void setPetType(String petType) {
this.petType = petType;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Pet [petType=" + petType + ", color=" + color + "]";
}
}
User.java
package com.sikiedu.bean;
public class User {
private Integer u_id;
private String u_username;
private String u_password;
//加入宠物字段
private Pet u_pet;
public User(String u_username,Pet u_pet) {
System.out.println("方法1:String,Pet");
this.u_username = u_username;
this.u_pet = u_pet;
}
public User(Integer u_username,Pet u_pet) {
System.out.println("方法2:Integer,Pet");
this.u_username = u_username.toString();
this.u_pet = u_pet;
}
public User(Pet u_pet,Integer u_username) {
System.out.println("方法3:Pet,Integer");
this.u_username = u_username.toString();
this.u_pet = u_pet;
}
//spring创建对象是通过空参构造执行的 空参构造方法必须加上,因为重写了有参构造方法
public User() {
super();
System.out.println("空参");
}
public Integer getU_id() {
return u_id;
}
public Pet getU_pet() {
return u_pet;
}
public void setU_pet(Pet u_pet) {
this.u_pet = u_pet;
}
public void setU_id(Integer u_id) {
this.u_id = u_id;
}
public String getU_username() {
return u_username;
}
public void setU_username(String u_username) {
this.u_username = u_username;
}
public String getU_password() {
return u_password;
}
public void setU_password(String u_password) {
this.u_password = u_password;
}
public void userInit() {
System.out.println("userInit");
}
public void userDestory() {
System.out.println("userDestory");
}
@Override
public String toString() {
return "User [u_id=" + u_id + ", u_username=" + u_username + ", u_password=" + u_password + ", u_pet=" + u_pet
+ "]";
}
}
set方法注入
<bean name="user" class="com.sikiedu.bean.User" scope="singleton" init-method="userInit" destroy-method="userDestory">
<property name="u_id" value="2"/>
<property name="u_username" value="雪风"/><!-- set方法注入值类型 -->
<property name="u_password" value="123"/>
<property name="u_pet" ref="dog"/>
</bean>
<!-- 注入引用类型 -->
<bean name="dog" class="com.sikiedu.bean.Pet">
<property name="petType" value="二哈"/>
<property name="color" value="灰白色"/>
</bean>
//set方法注入
@Test
public void Test6() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User u1=ac.getBean(User.class);
System.out.println(u1);
}
构造方法注入
<!-- 构造方法注入 如果出现多个构造方法重载,可以使用type index指定符合——位于index位置参数类型type的构造方法-->
<!-- name调用构造方法的参数名称 value是注入值类型 type是指定参数的类型 index是指定type参数的位置-->
<bean name="user1" class="com.sikiedu.bean.User">
<constructor-arg name="u_username" value="66" type="java.lang.Integer" index="0"/> <!--此时这个bean指定的就是方法3-->
<constructor-arg name="u_pet" ref="dog"/>
</bean>
//构造方法注入
@Test
public void Test7() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User u1=(User)ac.getBean("user1");
System.out.println(u1);
}
复杂类型注入
Array、List、Set、Map、Properties
<!-- 复杂类型注入 -->
<bean name="collection" class="com.sikiedu.bean.MyCollection">
<!-- array一个值使用方法 -->
<!-- <property name="array" value="123"></property> -->
<!-- array 多个值使用方法-->
<property name="array">
<array>
<value>123</value>
<value>abc</value>
<ref bean="dog"/>
</array>
</property>
<!-- list -->
<property name="list">
<list>
<value>456</value>
<value>cas</value>
<ref bean="user1"/>
</list>
</property>
<!-- set -->
<property name="set">
<set>
<value>782</value>
<value>www</value>
<ref bean="user1"/>
</set>
</property>
<!-- map -->
<property name="map">
<map>
<entry key="username" value="login"/>
<entry key="password" value="123"/>
<entry key-ref="user1" value-ref="dog"/>
</map>
</property>
<!-- peopertis -->
<property name="prop">
<props>
<prop key="name">雪风</prop>
<prop key="age">12</prop>
</props>
</property>
</bean>
注解配置
将对象注册到容器中
applicationContext_annotation.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-4.3.xsd">
<!-- 注解开发 -->
<!-- 开启组件扫描 base-package扫描该包下以及子包所有的注解-->
<context:component-scan base-package="com.sikiedu.bean"></context:component-scan>
<!-- 将pet对象交给spring容器管理 -->
<bean name="dog" class="com.sikiedu.bean.Pet">
<property name="petType" value="二哈"/>
<property name="color" value="灰白"/>
</bean>
</beans>
com.sikiedu.bean下的User2.java
package com.sikiedu.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//<bean name="user" class="com.sikiedu.bean.User">
/*将对象注册到容器*/
@Component("user")
//@Controller() //对应web层
//@Service("user") //对应service
//@Repository() //对应dao层
public class User2 {
@Value(value="1")
private Integer u_id; //使用暴力反射去注入的 所以推荐在set方法上注入
private String u_username;
private String u_password;
//加入宠物字段
private Pet u_pet;
public Pet getU_pet() {
return u_pet;
}
//自动装配
//@Autowired
//手动指定
@Resource(name="dog")
public void setU_pet(Pet u_pet) {
this.u_pet = u_pet;
}
public User2() {
System.out.println("User2 对象空参构造方法");
}
public Integer getU_id() {
return u_id;
}
public void setU_id(Integer u_id) {
this.u_id = u_id;
}
public String getU_username() {
return u_username;
}
@Value("雪风") //推荐在set方法上注入
public void setU_username(String u_username) {
this.u_username = u_username;
}
public String getU_password() {
return u_password;
}
public void setU_password(String u_password) {
this.u_password = u_password;
}
//标识在构造方法后调用
@PostConstruct()
public void userInit() {
System.out.println("user init ");
}
//标识在销毁方法前调用
@PreDestroy()
public void userDestroy() {
System.out.println("user destroy");
}
@Override
public String toString() {
return "User [u_id=" + u_id + ", u_username=" + u_username + ", u_password=" + u_password + ", u_pet=" + u_pet
+ "]";
}
}
Spring整合Junit单元测试
package com.sikiedu.test;
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sikiedu.bean.Pet;
//使用junit进行测试,帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//读取配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Test_Junit {
@Resource(name="dog")
private Pet p;
@Test
public void Test() {
System.out.println(p);
}
}
使用注解方式可以帮我们处理一些测试方法中重复且冗余的代码
在主配置文件导入其他配置文件
添加如下代码即可
<!-- 导入其他Spring的配置文件 -->
<import resource="/applicationContext_Injection.xml"/>
使用spring来效验用户登陆的案例
导入jar包
c3p0-0.9.5.5.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
commons-dbutils-1.6.jar
mchange-commons-java-0.2.19.jar
mysql-connector-java-8.0.16.jar
spring-beans-5.0.8.RELEASE.jar
spring-context-5.0.8.RELEASE.jar
spring-core-5.0.8.RELEASE.jar
spring-expression-5.0.8.RELEASE.jar
spring-web-5.0.8.RELEASE.jar
com.sikiedu.web下的UserLoginServlet.java
package com.sikiedu.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.sikiedu.bean.User;
import com.sikiedu.service.UserService;
/**
* Servlet implementation class UserLoginServlet
*/
@WebServlet("/userLogin")
public class UserLoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private UserService us;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 接收表单数据
String username = request.getParameter("username");
String password = request.getParameter("password");
// 封装成user对象
User u = new User();
u.setU_password(password);
u.setU_username(username);
// 通过容器获取userService 但是这样的作法不好,因为需要多次创建spring容器且没有销毁
// ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// us = (UserService) ac.getBean("userService");
// 在web项目中我们只需要一个spring的容器
// application域
// ServletContext() 生命周期 随着web项目启动而创建 随着web项目关闭而销毁
// ServletContextListener 可以通过配置监听器来达到我们的需求,在web项目创建时候创建spring容器,销毁时候关闭spring容器
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
us = (UserService) wac.getBean("userService");
// 调用service方法验证用户
User loginUser = us.getUserByInfo(u);
// 根据用户验证结果进行操作
if (loginUser == null) {
// 验证失败 转发到login_page.jsp
request.setAttribute("errorMsg", "用户名或密码错误");
request.getRequestDispatcher("/login_page.jsp").forward(request, response);
} else {
// 验证成功登陆,重定向到index.jsp
HttpSession session = request.getSession();
session.setAttribute("user", loginUser);
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
}
public UserLoginServlet() {
super();
// TODO Auto-generated constructor stub
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
com.sikiedu.service下的UserService.java
package com.sikiedu.service;
import com.sikiedu.bean.User;
import com.sikiedu.dao.UserDao;
public class UserService {
private UserDao ud;
public void setUd(UserDao ud) {
this.ud = ud;
}
public User getUserByInfo(User u) {
return ud.getUserByInfo(u);
}
}
com.sikiedu.dao下的UserDao.java
package com.sikiedu.dao;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.sikiedu.bean.User;
public class UserDao {
private ComboPooledDataSource dataSource;
public void setDataSource(ComboPooledDataSource dataSource) {
this.dataSource = dataSource;
}
//未使用spring时需要在静态代码块里配置dataSource
// private static ComboPooledDataSource dataSource;
// static {
// //链接数据库 使用c3p0
// try {
// dataSource = new ComboPooledDataSource();
// dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
// dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT");
// dataSource.setUser("login");
// dataSource.setPassword("123");
// }catch (Exception e) {
// e.printStackTrace();
// }
//
// }
public User getUserByInfo(User u) {
//通过数据库获取用户
try {
//使用dbutils操作数据库 查询并返回用户对象
QueryRunner qr = new QueryRunner(dataSource);
String sql = "select * from user where u_username=? and u_password =?";
return qr.query(sql, new BeanHandler<User>(User.class), u.getU_username(), u.getU_password());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
com.sikiedu.bean下的User.java
package com.sikiedu.bean;
public class User {
private Integer u_id;
private String u_username;
private String u_password;
/*各个属性的get和set方法,以及空参和有参构造器*/
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">
<!-- 配置dataSource -->
<bean name="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"></property>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="user" value="login"></property>
<property name="password" value="123"></property>
</bean>
<!-- 配置Dao -->
<bean name="userDao" class="com.sikiedu.dao.UserDao">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置service -->
<bean name="userService" class="com.sikiedu.service.UserService">
<property name="ud" ref="userDao"></property>
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>ssm_spring_servlet</display-name>
<!-- 配置监听器,在web项目启动的时候让spring启动 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 读取spring的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
</web-app>
Spring-aop
Proxy动态代理:被代理的对象必须要实现接口
com.sikiedu.service包
UserService.java接口
package com.sikiedu.service;
public interface UserService {
//增
void save();
//删
void delete();
//改
void update();
//查
void find();
}
UserServiceImpl.java实现类
package com.sikiedu.service;
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("save");
}
@Override
public void delete() {
System.out.println("delete");
}
@Override
public void update() {
System.out.println("update");
}
@Override
public void find() {
System.out.println("find");
}
}
com.sikiedu.test包
AopTest.java
package com.sikiedu.test;
import org.junit.jupiter.api.Test;
import com.sikiedu.service.UserService;
import com.sikiedu.service.UserServiceImpl;
public class AopTest {
@Test
public void Test1(){
UserServiceProxy usProxy = new UserServiceProxy();
UserService us =new UserServiceImpl();
UserService us_PowerUp = usProxy.getUserServiceProxy(us);
us_PowerUp.find();
}
}
UserServiceProxy.java
package com.sikiedu.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.sikiedu.service.UserService;
import com.sikiedu.service.UserServiceImpl;
/**
* UserService代理类
* @author
*
*/
public class UserServiceProxy {
public UserService getUserServiceProxy(UserService us) {
return (UserService) Proxy.newProxyInstance(UserServiceProxy.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强代码
System.out.println("开启事务");
//调用原始的方法
Object invoke = method.invoke(us, args);
//增强代码
System.out.println("提交/回滚");
return invoke;
}
});
}
}
控制台输出
开启事务
find
提交/回滚
使用xml文件来配置spring-aop
创建com.sikiedu.aop包下的MyAdvice.java
package com.sikiedu.aop;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 自定义通知类
*/
public class MyAdvice {
//before 前置通知 在目标方法前调用
public void before() {
System.out.println("before");
}
//after 最终通知(后置通知)在目标方法后调用,无论是否出现异常都会执行 finally
public void after() {
System.out.println("after");
}
//afterReturning 成功通知(后置通知)在目标方法执行后,并且执行成功,如果方法出现异常则不调用
public void afterReturning() {
System.out.println("afterReturning");
}
//afterThrowing 异常通知(后置通知)在目标方法执行出现异常的时候才会调用
public void afterThrowing() {
System.out.println("afterThrowing");
}
//around 环绕通知 需要我们手动调用目标方法,并且可以设置通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before");
Object proceed = pjp.proceed();
System.out.println("around after");
return proceed;
}
}
在applicationContext.xml文件中添加配置切入点和五种通知
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" 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-4.3.xsd">
<!-- 目标对象 -->
<bean name="userService" class="com.sikiedu.service.UserServiceImpl"></bean>
<!-- 通知对象 -->
<bean name="myAdvice" class="com.sikiedu.aop.MyAdvice"></bean>
<aop:config>
<!-- 切入点 expression 切入点表达式 可以配置要增强的方法 public void com.sikiedu.service.UserServiceImpl.save() * com.sikiedu.service.*ServiceImpl.*(..) id 就是唯一标识 -->
<aop:pointcut expression="execution(* com.sikiedu.service.*ServiceImpl.*(..))" id="servicePc" />
<!-- 切面 通知+切入点 -->
<aop:aspect ref="myAdvice">
<!-- 通知类型 -->
<aop:before method="before" pointcut-ref="servicePc" />
<!-- 最终通知 后置通知 -->
<aop:after method="after" pointcut-ref="servicePc" />
<!-- 成功通知 后置通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="servicePc" />
<!-- 异常通知 后置通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="servicePc" />
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="servicePc" />
</aop:aspect>
</aop:config>
</beans>
在AopTest.java中添加方法测试
package com.sikiedu.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sikiedu.service.UserService;
import com.sikiedu.service.UserServiceImpl;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Test
public void Test1() {
UserServiceProxy usProxy = new UserServiceProxy();
UserService us = new UserServiceImpl();
UserService us_PowerUp = usProxy.getUserServiceProxy(us);
us_PowerUp.find();
}
@Resource(name="userService")
UserService us;
@Test
public void Test2() {
us.delete();
}
}
运行Test2方法控制台打印
before
around before
delete
around after
afterReturning
after
Spring使用JdbcTemplate
导入相关jar包
c3p0-0.9.5.5.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
mchange-commons-java-0.2.19.jar
mysql-connector-java-8.0.16.jar
spring-aop-5.0.8.RELEASE.jar
spring-beans-5.0.8.RELEASE.jar
spring-context-5.0.8.RELEASE.jar
spring-core-5.0.8.RELEASE.jar
spring-expression-5.0.8.RELEASE.jar
spring-jdbc-5.0.8.RELEASE.jar
spring-test-5.1.8.RELEASE.jar
spring-tx-5.0.8.RELEASE.jar
创建com.sikiedu.bean下的User.java
package com.sikiedu.bean;
public class User {
private Integer u_id;
private String u_username;
private String u_password;
/* 各个属性的get和set方法以及空参和有参构造器以及toString方法*/
}
创建com.sikiedu.dao包
接口UserDao.java
package com.sikiedu.dao;
import java.util.List;
import com.sikiedu.bean.User;
public interface UserDao {
//增
void saveUser(User u);
//删
void deleteUserById(Integer id);
//改
void updateUser(User u);
//id查找用户
User selectUserById(Integer id);
//查找用户List
List<User> selectAllUser();
//查找用户数量s
Integer selectUserCount();
}
实现类UserDaoImpl.java
package com.sikiedu.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.sikiedu.bean.User;
public class UserDaoImpl extends JdbcDaoSupport implements UserDao{
// JdbcTemplate jt;
// 注入自己配置的JdbcTemplate
// public void setJt(JdbcTemplate jt) {
// this.jt = jt;
// }
//保存用户
@Override
public void saveUser(User u) {
String sql = "insert into user values(null,?,?)";
getJdbcTemplate().update(sql, u.getU_username(), u.getU_password());
}
//根据id删除用户
@Override
public void deleteUserById(Integer id) {
String sql="delete from user where u_id =?";
getJdbcTemplate().update(sql, id);
}
//更新用户
@Override
public void updateUser(User u) {
String sql="update user set u_username=?, u_password=? where u_id =?";
getJdbcTemplate().update(sql, u.getU_username(), u.getU_password(), u.getU_id());
}
//根据id查询一个用户
@Override
public User selectUserById(Integer id) {
String sql = "select * from user where u_id = ?";
User user = getJdbcTemplate().queryForObject(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int index) throws SQLException {
User u =new User();
u.setU_id(rs.getInt("u_id"));
u.setU_username(rs.getString("u_username"));
u.setU_password(rs.getString("u_password"));
return u;
}
}, id);
return user;
}
//查询用户列表
@Override
public List<User> selectAllUser() {
String sql = "select * from user";
List<User> list = getJdbcTemplate().query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int index) throws SQLException {
User u =new User();
u.setU_id(rs.getInt("u_id"));
u.setU_username(rs.getString("u_username"));
u.setU_password(rs.getString("u_password"));
return u;
}
});
return list;
}
//查询用户数量
@Override
public Integer selectUserCount() {
String sql = "select count(*) from user";
return getJdbcTemplate().queryForObject(sql, Integer.class);
}
}
创建测试类JdbcTest.java
package com.sikiedu.test;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sikiedu.bean.User;
import com.sikiedu.dao.UserDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTest {
@Resource(name="userDao")
private UserDao ud;
@Test
public void saveUser() {
User u = new User();
u.setU_username("jifeng");
u.setU_password("123");
ud.saveUser(u);
}
@Test
public void updateUserById() {
User u = new User();
u.setU_id(2);
u.setU_username("alice.alice");
u.setU_password("234");
ud.updateUser(u);
}
@Test
public void findUserById() {
User u = ud.selectUserById(3);
System.out.println(u);
}
@Test
public void findAllUser() {
List<User> u_list = ud.selectAllUser();
for (User user : u_list) {
System.out.println(user);
}
}
@Test
public void findCountUser() {
Integer count = ud.selectUserCount();
System.out.println(count);
}
@Test
public void deleteUserById() {
ud.deleteUserById(2);
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 依赖关系 dao -> -> dataSource -->
<!-- 读取配置文件 -->
<context:property-placeholder location="db.properties"/>
<!-- 配置 dataSource -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 自己配置jdbcTemplate -->
<!-- <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean> -->
<!-- dao -->
<bean name="userDao" class="com.sikiedu.dao.UserDaoImpl">
<!-- <property name="jt" ref="jdbcTemplate"/> -->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
db.properties
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
jdbc.user=login
jdbc.password=123
Spring-aop事务
事务基本知识
1、 事务相关知识:
a) 什么是事务:把多条数据库操作捆绑到一起执行,要么都成功,要么都失败;
b) 事务的原则ACID:
i. 原子性:事务包含的所有操作,要么全部成功,要么全部失败回滚,成功全部应用到数据库,失败不能对数据库有任何影响;
ii. 一致性:事务在执行前和执行后必须一致;例如A和B一共有100块钱,无论A、B之间如何转账,他们的钱始终相加都是100;
iii. 隔离性:多用户并发访问同一张表时,数据库为每一个用户开启新的事务,该事务不能被其他事务所影响,相互有隔离;
iv. 持久性:一个事务一旦提交,则对数据库中数据的改变是永久的,即便系统故障也不会丢失;
c) 并发可能引起的问题:
i. 脏读:一个事务读取到另一个事务未提交的数据;
ii. 不可重复读:一个事务读取到另一个事务已提交(Update操作)的数据,导致前后读取不一致;
iii. 幻读(虚读):一个事务中读取到别的事务插入(Insert操作)的数据,导致前后读取不一致;
d) 事务的隔离级别:根据实际情况选择;
i. Serializable串行化:可避免脏读、不可重复读和幻读;
ii. Repeatable read可重复读:可避免脏读、不可重复读;(MySql默认值)
iii. Read committed读已提交:可避免脏读;
Ⅳ Read uncommitted 读未提交:任何情况都无法保证
经典转账案例
a) 使用经典的转账案例进行测试,准备数据:bean、service、dao;
b) 使用事务需要额外导入tx包和tx约束;
c) 配置事务核心管理器: DataSourceTransactionManager;
d) 配置事务通知 tx:Advice;
e) 配置aop;
通过xml方式配置事务
导入相关的jar包
c3p0-0.9.5.5.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
mchange-commons-java-0.2.19.jar
mysql-connector-java-8.0.16.jar
spring-aop-5.0.8.RELEASE.jar
spring-beans-5.0.8.RELEASE.jar
spring-context-5.0.8.RELEASE.jar
spring-core-5.0.8.RELEASE.jar
spring-expression-5.0.8.RELEASE.jar
spring-jdbc-5.0.8.RELEASE.jar
spring-test-5.1.8.RELEASE.jar
spring-tx-5.0.8.RELEASE.jar
创建bean包下的Account.java
package com.sikiedu.bean;
public class Account {
private Integer id;
private String name;
private Double money;
/* 各个属性的get和set方法以及空参和有参构造器以及toString方法*/
}
创建AccountDao.java接口和AccountDaoImpl.java实现类
package com.sikiedu.dao;
public interface AccountDao {
//扣钱
void subMoney(Integer id, Double money);
//加钱
void addMoney(Integer id, Double money);
}
package com.sikiedu.dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void subMoney(Integer id, Double money) {
String sql= "update account set money = money - ? where id =?";
getJdbcTemplate().update(sql, money, id);
}
@Override
public void addMoney(Integer id, Double money) {
String sql= "update account set money = money + ? where id =?";
getJdbcTemplate().update(sql, money, id);
}
}
创建AccountService.java接口和AccountServiceImpl.java实现类
package com.sikiedu.service;
public interface AccountService {
//转账接口
void transferAccounts();
}
package com.sikiedu.service;
import com.sikiedu.dao.AccountDao;
public class AccountServiceImpl implements AccountService {
//账户dao
private AccountDao ad;
public void setAd(AccountDao ad) {
this.ad = ad;
}
@Override
public void transferAccounts() {
//先从A账户扣款
ad.subMoney(1,50d);
//int a= 1/0;
//再给B账户扣款
ad.addMoney(2,50d);
}
}
配置applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 依赖关系 dao -> -> dataSource -->
<!-- 读取配置文件 -->
<context:property-placeholder location="db.properties"/>
<!-- 配置 dataSource -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- dao -->
<bean name="accountDao" class="com.sikiedu.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- service -->
<bean name="accountService" class="com.sikiedu.service.AccountServiceImpl">
<property name="ad" ref="accountDao"/>
</bean>
<!-- 配置事务核心管理器 不同平台不一样 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 事务通知 -->
<tx:advice id="txAdivce" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transferAccounts" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="select*" isolation="DEFAULT" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<aop:pointcut expression="execution(* com.sikiedu.service.*ServiceImpl.*(..))" id="txPc"/>
<aop:advisor advice-ref="txAdivce" pointcut-ref="txPc"/>
</aop:config>
</beans>
db.properties
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
jdbc.user=login
jdbc.password=123
添加测试方法
package com.sikiedu.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sikiedu.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TxTest {
@Resource(name="accountService")
private AccountService as;
@Test
public void Test1() {
as.transferAccounts();
}
}
通过注解方式配置事务
修改AccountServiceImpl.java
package com.sikiedu.service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.sikiedu.dao.AccountDao;
//给类添加注解 类下所有方法都有效
@Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED, readOnly=true)
public class AccountServiceImpl implements AccountService {
//账户dao
private AccountDao ad;
public void setAd(AccountDao ad) {
this.ad = ad;
}
@Override
@Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED, readOnly=false)
public void transferAccounts() {
//转账逻辑
//先从A账户扣款
ad.subMoney(1, 50d);
int a = 1/0;
//再给B账户加款
ad.addMoney(2, 50d);
}
}
修改applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 依赖关系 dao -> -> dataSource -->
<!-- 读取配置文件 -->
<context:property-placeholder location="db.properties"/>
<!-- 配置 dataSource -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- dao -->
<bean name="accountDao" class="com.sikiedu.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- service -->
<bean name="accountService" class="com.sikiedu.service.AccountServiceImpl">
<property name="ad" ref="accountDao"/>
</bean>
<!-- 配置事务核心管理器 不同平台不一样 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven/>
</beans>
Spring整合Mybatis框架
导入相关jar包
aop相关
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-5.0.8.RELEASE.jar
spring-aspects-5.0.8.RELEASE.jar
spring基本包
com.springsource.org.apache.commons.logging-1.1.1.jar
spring-beans-5.0.8.RELEASE.jar
spring-context-5.0.8.RELEASE.jar
spring-core-5.0.8.RELEASE.jar
spring-expression-5.0.8.RELEASE.jar
事务
spring-jdbc-5.0.8.RELEASE.jar
spring-tx-5.0.8.RELEASE.jar
数据库驱动
c3p0-0.9.5.5.jar
mchange-commons-java-0.2.19.jar
mysql-connector-java-8.0.16.jar
mybatis核心包
mybatis-3.4.6.jar
mybatis与spring整合包
mybatis-spring-1.3.2.jar
spring的Junit的test包
spring-test-5.1.8.RELEASE.jar
com.sikiedu.bean下的Account.java
package com.sikiedu.bean;
public class Account {
private Integer id;
private String name;
private Double money;
//转账金额 不在数据库中
private Double tranferMoney;
/*属性的get和set方法*/
public Account(Integer id, String name, Double money) {
super();
this.id = id;
this.name = name;
this.money = money;
}
public Account() {
super();
}
}
com.sikiedu.mapper
AccountMapper.java接口
package com.sikiedu.mapper;
import com.sikiedu.bean.Account;
//账户mapper接口
public interface AccountMapper {
//操作数据库 扣款和加款
//扣
void subMoney(Account pay);
//加
void addMoney(Account collect);
}
AccountMapper.xml
<?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">
<mapper namespace="com.sikiedu.mapper.AccountMapper">
<update id="subMoney" parameterType="Account">
update account set money = money - #{tranferMoney} where id = #{id}
</update>
<update id="addMoney" parameterType="Account">
update account set money = money + #{tranferMoney} where id = #{id}
</update>
</mapper>
com.sikiedu.service
AccountService.java接口
package com.sikiedu.service;
public interface AccountService {
//未配置事务的方法
void tranferAccount();
//配置事务的方法
void updateTranferAccount();
}
AccountServiceImpl.java
package com.sikiedu.service;
import javax.annotation.Resource;
import com.sikiedu.bean.Account;
import com.sikiedu.mapper.AccountMapper;
public class AccountServiceImpl implements AccountService {
@Resource(type = AccountMapper.class)
private AccountMapper mapper;
//未使用事务的方法
@Override
public void tranferAccount() {
Double tranferMoney = 1000d;
Account pay = new Account();
pay.setId(1);
pay.setTranferMoney(tranferMoney);
// 先扣
mapper.subMoney(pay);
//异常
int a=1/0;
Account collect = new Account();
collect.setId(2);
collect.setTranferMoney(tranferMoney);
// 再加
mapper.addMoney(collect);
}
//配置事务的方法
@Override
public void updateTranferAccount() {
Double tranferMoney = 1000d;
Account pay = new Account();
pay.setId(1);
pay.setTranferMoney(tranferMoney);
// 先扣
mapper.subMoney(pay);
//异常
int a=1/0;
Account collect = new Account();
collect.setId(2);
collect.setTranferMoney(tranferMoney);
// 再加
mapper.addMoney(collect);
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 读取配置文件 -->
<context:property-placeholder location="db.properties"/>
<!-- 配置 dataSource -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- mybatis -->
<bean name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:sqlMapConfig.xml"/>
</bean>
<!-- mapper工厂 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.sikiedu.mapper"/>
</bean>
<!-- service -->
<bean name="accountService" class="com.sikiedu.service.AccountServiceImpl"></bean>
<!-- 需要事务核心管理器 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<aop:pointcut expression="execution(* com.sikiedu.service.*ServiceImpl.*(..))" id="txPc"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPc"/>
</aop:config>
</beans>
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>
<typeAliases>
<package name="com.sikiedu.bean"/>
</typeAliases>
</configuration>
db.properties
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
jdbc.user=login
jdbc.password=123
测试类MapperTest.java
package com.sikiedu.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.sikiedu.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MapperTest {
@Resource(name="accountService")
private AccountService as;
@Test
public void Test1() {
as.tranferAccount();
}
}