@[TOC](Spring框架学习笔记(二))
七、JdbcTemplate工具类
Spring中提供了对于JDBC操作的封装,复习JDBC操作数据库步骤:
- 加载JDBC驱动(面向接口编程,有SUN公司提供了操作数据库的标准接口,各个数据库厂商实现这套接口):驱动包
- 建立数据库连接(因为每次都需要建立,消耗资源,推荐使用开源数据库连接池)
- 可变部分:设置指令(SQL语句、函数、存储过程等)
- 获取执行SQL语句的对象(
Statement
和PrepareStatement
)- 变更数据操作:
executeUpdate
方法,默认情况下,事务是自动提交 - 查询操作:JDBC将查询的结果(虚拟表)封装成ResultSet对象,我们可以对该对象进行二次处理
- 变更数据操作:
- 关闭资源
JdbcTemplate封装了JDBC的基本操作,默认情况下事务自动提交
<dependencies>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
封装jdbc操作
@Configuration
public class SpringConfig01 {
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/yf07_mybatis");
druidDataSource.setUsername("root");
druidDataSource.setPassword("");
return druidDataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
}
常用方法介绍
public class SpringTest {
private JdbcTemplate jdbcTemplate;
@Before
//初始化前执行
public void init(){
//加载配置类
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig01.class);
jdbcTemplate = ac.getBean(JdbcTemplate.class);//按照类型获取
}
//获取列表数据List<Map<String,Object>>虚拟表中的字段作为Map中的KEY
@Test
public void test01(){
//如果动态的SQL语句,那么这个逻辑判断都是由开发人员通过代码控制
//名字包含“僧”的数据
String sql = "SELECT * FROM student WHERE student_name LIKE CONCAT('%',?,'%')";
List<Map<String, Object>> list = this.jdbcTemplate.queryForList(sql, "僧");
for (Map<String, Object> map : list) {
System.out.println("map = " + map);
}
}
@Test
public void test02(){
//查询指定student_id的数据
String sql = "SELECT * FROM student WHERE student_id=?";
Map<String, Object> map = this.jdbcTemplate.queryForMap(sql, 10);
System.out.println("map = " + map);
}
@Test
public void test03(){
//查询指定student_id的数据中student_name单个值
String sql = "SELECT student_name FROM student WHERE student_id=?";
String studentName = this.jdbcTemplate.queryForObject(sql, String.class, 10);
System.out.println("studentName = " + studentName);
//查询记录总数
sql = "SELECT COUNT(*) FROM student ";
Integer count = this.jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("count = " + count);
}
}
@Test
public void test04(){
//插入数据 可以参与计算
String sql = "INSERT INTO student (student_name,student_sex,age,birthday) VALUES (?,?,?+18,?)";
this.jdbcTemplate.update(sql,"糖糖","女",18,"1990-09-09");
}
@Test
public void test05(){
String sql = "UPDATE student SET age=age-?";
this.jdbcTemplate.update(sql,10);
}
query
方法中需要实现RowMapper
接口
因为获取date类型数据的时候默认
sql.date
,而不是util.date
,所以使用rs.getObject
获取date数据
- 匿名实现类
@Test
public void test06(){
String sql = "SELECT * FROM student WHERE student_name LIKE CONCAT('%',?,'%')";
List<Student> list = this.jdbcTemplate.query(sql, new RowMapper<student>(){
@Override
public Student mapRow(ResultSet rs, int i) throws SQLException {
Student student = new Student();
student.setStudentId(rs.getInt("student_id"));
student.setStudentName(rs.getString("student_name"));
student.setAge(rs.getInt("age"));
student.setBirthday((Date) rs.getObject("birthday"));
return student;
}
}, "僧");//匿名实现类
for (Student student : list) {
System.out.println("student = " + student);
}
}
}
- 自己建立实现类,提取公共部分
@Test
public void test07(){
String sql = "SELECT * FROM student WHERE student_id=?";
Student student = this.jdbcTemplate.queryForObject(sql,new StudentRowMapper(),10);
System.out.println("student = " + student);
}
class StudentRowMapper implements RowMapper<Student>{
@Override
public Student mapRow(ResultSet rs, int i) throws SQLException {
Student student = new Student();
student.setStudentId(rs.getInt("student_id"));
student.setStudentName(rs.getString("student_name"));
student.setAge(rs.getInt("age"));
student.setBirthday((Date) rs.getObject("birthday"));
return student;
}
程序员懒,所有实现类都不想写,所以简化,前提为遵循约定,自动处理:虚拟表中的字段跟类中属性一致 用
BeanPropertyRowMapper
方法
比较老的系统会使用它,现在基本见不到了
@Test
public void test08(){
String sql = "SELECT student_id studentId,student_name studentName,student_sex studentSex,age,birthday FROM student WHERE student_name LIKE CONCAT('%',?,'%')";
List<Student> list = this.jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class), "乔");//匿名实现类
for (Student student : list) {
System.out.println("student = " + student);
}
}
}
八、代理模式
代理模式是一种常见设计模式,并且代理模式是一种思维方式。
前文相关
定义:给某个对象提供一个代理,并且有代理对象控制原对象的引用。
8.1 静态代理
静态代理特别简单,是由程序员编写代理类,并且在程序运行前就编译好,而不是由程序动态产生的代理类,所谓静态代理。
- 目标类(被代理类)
package com.yue.service;
//目标类(被代理类)
public class UserServiceImpl implements UserService {
//核心代码已经固定,但是客户增加新的需求
//假设需求:输入形参的数据
@Override
public void add(int age, String account) {
System.out.println("完成用户的添加操作,固定的核心代码");
}
@Override
public String update(String userName) {
return userName+userName;
}
}
如果需求改变,我们可能就需要修改核心代码,就有可能给核心代码造成错误
- 静态代理类
目的就是创建代理对象
不能让代理看到了核心代码,这是不合理的
不改变原来的核心代码并且增加了新的功能
package com.yue.proxy;
import com.yue.service.UserService;
import com.yue.service.UserServiceImpl;
//代理类是由程序员自己完成
//代理类=>创建代理对象=>代表被代理的对象(目标对象)
public class UserStaticProxy implements UserService {
private UserServiceImpl target = new UserServiceImpl();//目标对象
@Override
public void add(int age, String account) {
//不合理的,因为代理看到了核心代码(又重写一遍)
//System.out.println("完成用户的添加操作,固定的核心代码");
//新增代码,没有改变核心代码
System.out.println("age = " + age);
target.add(age,account);//引用目标对象
System.out.println("account = " + account);
}
@Override
public String update(String userName) {
System.out.println("userName = " + userName);
String result = this.target.update(userName);
return result;
}
}
缺点:当新增另一个更新功能的时候,接口一增加,目标类和代理类全都要增加
8.2 Java动态代理(必须提供接口)
代理类在程序运行时创建的代理方式被称为动态代理。
动态代理,并不是在Java代码中定义,而是在运行时根据我们Java代码中的“指示”动态生成。相比静态代理,动态代理的优势在于可以很方便的代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
代理步骤(固定套路):
- 定义一个事务管理类,实现
InvocationHadler
接口,并且实现invoke
(代理类,被代理的方法,方法的形参列表)方法 - 实现被代理类以及其实现的接口(Java动态代理,必须存在接口和实现类)
- 调用
Proxy.newProxyInstance(类的加载器,类实现的接口,事务处理器对象)
生成代理对象 - 通过该代理对象调用方法
InvocationHandler作用就是,当代理对象的原本方法被调用的时候,会绑定执行一个方法,这个方法就是InvocationHandler里面定义的内容,同时会替代原本方法的结果返回。
它接受三个参数:
proxy
:代理后的实例对象。method
:对象被调用方法。args
:调用时的参数。
- 事务管理类
目的:在运行期间动态生成代理类
package com.yue.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
//运行期间动态生成代理类
public class JavaDynamicProxy implements InvocationHandler {
private Object target;//目标对象,被代理对象
public JavaDynamicProxy(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("新增代码:参数列表 = " + Arrays.toString(args));//统一的处理
System.out.println("代理的方法:"+method.getName());
Object result = null;
result = method.invoke(target,args);
return result;
}
}
@Test
public void test02(){
//被代理对象
UserServiceImpl userService = new UserServiceImpl();
//创建代理类
JavaDynamicProxy javaDynamicProxy = new JavaDynamicProxy(userService);
//创建代理对象
//Proxy.newProxyInstance(类的加载器,类实现的接口,事务处理器对象)
UserService proxy =(UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
javaDynamicProxy
);
//代理对象调用
proxy.add(18,"齐天大圣孙悟空");
String result = proxy.update("八戒");
System.out.println("result = " + result);
}
结果:
演示Java动态代理,普通的类,报错:
java.lang.ClassCastException: com.sun.proxy.$Proxy4 cannot be cast to com.yue.service.RoleController
public class RoleController {
public int sum(int a,int b){
return a+b;
}
}
此时便需要CGLIB动态代理
8.3 CGLIB(Code Generation Library
)动态代理(普通java类不需要接口)
注意:
MethodInterceptor
是AOP项目中的拦截器(注:不是动态代理拦截器)
与HandlerInterceptor
拦截器的区别在于后者拦截目标的请求,它拦截的目标是方法。
对类实现代理,对执行目标类(被代理类)动态的生成一个子类(继承方式),子类会重写父类中的方法(增强的代码),所以,被代理的类必须是可以被继承的,被代理的类不能使用
final
修饰
- 在pom.xml中添加
CGLIB
第三方支持
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
依赖图:
使用生成子类的方式,也就是继承,去实现动态代理
//动态生成代理类(生成子类方式=继承)
public class CGLIBDynamicProxy implements MethodInterceptor {
//创建代理对象
public Object getProxy(Class<?> targetClass){
//1.创建子类(增强代码)
Enhancer enhancer = new Enhancer();
//2.设置子类是继承哪个父类
enhancer.setSuperclass(targetClass);
//3.定义代理逻辑对象,必须实现MethodInterceptor接口
enhancer.setCallback(this);
//4.创建子类对象(代理对象)
Object proxy = enhancer.create();
return proxy;
}
/**
* 拦截所有目标类(被代理类)方法的调用
* @param target 目标类的实例对象
* @param method 目标方法的反射对象
* @param args 方法传递参数
* @param proxy 代理类的实例
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("新增代码CGLIB:参数列表 = " + Arrays.toString(args));//统一的处理
Object result = null;
result = proxy.invokeSuper(target,args);
return result;
}
}
@Test
public void test04(){
CGLIBDynamicProxy cglibDynamicProxy = new CGLIBDynamicProxy();
RoleController roleController = (RoleController)cglibDynamicProxy.getProxy(RoleController.class);
int result01 = roleController.sum(10,20);
System.out.println("result01 = " + result01);
UserServiceImpl userService = (UserServiceImpl)cglibDynamicProxy.getProxy(UserServiceImpl.class);
String result02 = userService.update("八戒");
System.out.println("result02 = " + result02);
}
8.4 代理对象
代理对象=增强代码+目标对象(被代理的对象)
,增强代码的可以写在什么位置?
package com.yue.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;
//动态生成代理类(生成子类方式=继承)
public class CGLIBDynamicProxy implements MethodInterceptor {
//创建代理对象
public Object getProxy(Class<?> targetClass){
//1.创建子类(增强代码)
Enhancer enhancer = new Enhancer();
//2.设置子类是继承哪个父类
enhancer.setSuperclass(targetClass);
//3.定义代理逻辑对象,必须实现MethodInterceptor接口
enhancer.setCallback(this);
//4.创建子类对象(代理对象)
Object proxy = enhancer.create();
return proxy;
}
/**
* 拦截所有目标类(被代理类)方法的调用
* @param target 目标类的示例对象
* @param method 目标方法的反射对象
* @param args 方法传递参数
* @param proxy 代理类的实例
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置增强:无论代码是否正确,都会执行");
System.out.println("新增代码CGLIB:参数列表 = " + Arrays.toString(args));//统一的处理
Object result = null;
try {
result = proxy.invokeSuper(target,args);
System.out.println("返回增强:代码正确的情况,才会执行");
System.out.println(target.getClass().getSuperclass().getName());
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("异常增强:代码错误的时候,才会执行" );
}
System.out.println("后置增强:如果异常没有控制(return/手动抛出异常),那么一定执行");
return result;
}
}