B029-JDBC增强

news/2024/5/19 1:22:53 标签: JDBC

目录

      • PreparedStatement 查询
        • 1.sql注入
        • 2.Statement分析 (面试题)
        • 3.PreparedStatement (面试题)
      • 登录功能的完善
      • 事务
      • 链接池
        • 概念
        • 实现
          • DBCP连接池实现
            • 第一种配置方式
            • 第二种配置方式
      • 返回主键
      • BaseDao的抽取

PreparedStatement 查询

1.sql注入

就是在sql的字符串拼接的时候,加入了特定的条件判断,

如:SELECT * FROM student where name=’ 小坤坤255255 ’ OR 1=1

代码

public class StudentDaoImpl  implements IStudentDao{
	//Statement的写法
	@Override
	public Student login(String name, String Password) {
		//通过工具类获取连接
		Connection conn = JDBCUtil.Instance().getconn();
		Statement State =null;
		ResultSet rs=null;
		Student student = new Student();
		try {
			 State = conn.createStatement();
			  rs = State.executeQuery("select * from student where name='"+name+"'and password ='"+Password+"'");
		      while (rs.next()) {
		    	  student.setId(rs.getInt("id"));
		    	  student.setName(rs.getString("name"));
		    	  student.setPassword(rs.getString("password"));
			} 
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			JDBCUtil.Instance().close(rs, State, conn);
		}
		return student;
	}
}
public class JDBCTest {
	@Test
	public void testName() throws Exception {
		StudentDaoImpl studentDaoImpl = new  StudentDaoImpl();
		//正常的代码
//		Student stu = studentDaoImpl.login("网通", "123");
		//sql注入的代码
		Student stu = studentDaoImpl.login("网通", "123' or '1=1");
		System.out.println(stu);
		if(stu.getName()!=null){
			System.out.println("账号存在登录成功");
		}else{
			System.out.println("账号不存在  ..登录失败");
		}
	}
}
2.Statement分析 (面试题)

1.通过上面sql注入的案例我们发现 Statement 它可能会导致sql注入的问题
2.通过这几天的sql的书写我发现 Statement 拼接sql相当复杂,稍微有一点点差错就会导致sql语句有问题

解决方法:PreparedStatement

3.PreparedStatement (面试题)

PreparedStatement 很好的解决了Statement的问题
1.不用担心注入问题(双引号之内看成一个整体的字符串而不是两个字符串和一个关键字),
2.sql语句不用复杂拼接,
3.会预处理sql语句,执行速度也更快

代码:
StudentDaoImpl

	//PreparedStatement写法
	@Override
	public Student login(String name, String Password) {
		 Connection conn = JDBCUtil2.Instance().getconn();
		 PreparedStatement ps=null;
		 ResultSet rs =null;
		 Student student = new Student();
		 try {
			 ps= conn.prepareStatement("select * from student  where name=? and password=?");
			 ps.setString(1, name);
			 ps.setString(2, Password);
			 rs = ps.executeQuery();
			 while(rs.next()){
				 student.setId(rs.getInt("id"));
				 student.setName(rs.getString("name"));
				 student.setPassword(rs.getString("password"));
			 }
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			JDBCUtil2.Instance().close(rs, ps, conn);
		}
	return student;
	}

JDBCTest

	@Test
	public void testName() throws Exception {
		StudentDaoImpl studentDaoImpl = new  StudentDaoImpl();
		//正常的代码
//		Student stu = studentDaoImpl.login("网通", "123");
		//sql注入的代码
		Student stu = studentDaoImpl.login("网通", "123' or '1=1");
		System.out.println(stu);
		if(stu.getName()!=null){
			System.out.println("账号存在登录成功");
		}else{
			System.out.println("账号不存在  ..登录失败");
		}
	}

问题:PreparedStatement和Statement 不是同一个类为什么关资源的时候可以传PreparedStatement
因为 PreparedStatement 继承了 Statement,(多态)

PreparedStatement :

  // 预处理 这时候就会把sql发送到数据库了,只是这时还不会执行sql
  select * from student  where name=? and password=? 		//变量位置使用?先占住,(这时已经发了sql语句了)
  ps= conn.prepareStatement("select * from student  where name=? and password=?");
  // 把?替换成对应的值
  ps.setString(1, name);
  ps.setString(2, Password);
  // 执行sql  这时的执行就是一个执行命令,不会发sql语句(前面已发)
  rs = ps.executeQuery();

Statement:

  //创建 Statement 对象 
  State = conn.createStatement();
  // 发送并执行sql 
  rs = State.executeQuery("select * from student where name='"+name+"'and password ='"+Password+"'");

PreparedStatement 是 Statement 子类,速度比Statement 快,能避免sql 注入,可以不用拼接sql语句

登录功能的完善

StudentDaoImpl

	@Override
	public Student QueryByUsername(String name) {
		Connection conn = JDBCUtil2.Instance().getconn();
		PreparedStatement ps = null;
		ResultSet rs = null;
		Student student = new Student();
		try {
			ps = conn.prepareStatement("select * from student where name=?");
			ps.setString(1, name);
			rs = ps.executeQuery();
			while (rs.next()) {
				student.setId(rs.getInt("id"));
				student.setName(rs.getString("name"));
				student.setPassword(rs.getString("password"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtil2.Instance().close(rs, ps, conn);
		}
		return student;
	}

JDBCTest

	// 登录的第二种实现方式
	@Test
	public void login() throws Exception {
		StudentDaoImpl studentDaoImpl = new StudentDaoImpl();
		// 查询是小坤坤(用户名)的信息,这个用户名 应该是前台(浏览器) 用户 传过来的 -- 模拟
		Student student = studentDaoImpl.QueryByUsername("小坤坤");
		// 判断用户名是否存在
		if (student.getName() == null) {
			System.out.println("账号不存在");
		}
		// else 就是账号存在
		else {
			// 判断这个账号的密码是否正确 (这个密码应该是前台(浏览器) 用户 传过来的)
			if (!"8848".equals(student.getPassword())) {
				System.err.println("密码错误");
			} else {
				System.out.println("登录成功");
			}
		}
	}

事务

	@Test
	public void Testtrans() throws Exception {
		Connection connection = null;
		PreparedStatement ps = null;
		PreparedStatement ps2 = null;
		try {
			connection = JDBCUtil2.Instance().getconn();
			// 不提交事务 (sql执行了,改变了数据库的数据,但是后面没有写提交事务数据库就不能有变化),
			connection.setAutoCommit(false);
			String sql = "update bank set money=money-1000 where name='过儿'";
			ps = connection.prepareStatement(sql);
			ps.execute();
			// 在这个位置 出现异常
			int a=0/0;
			String sql2 = "update bank set money=money+1000 where name='姑姑'";
			ps2 = connection.prepareStatement(sql2);
			ps2.execute();
			// 提交事物 (数据库可以发生变化了)
			connection.commit();
		} catch (Exception e) {
			// 回滚 (你数据库改变了之后我还是可以回滚)
			/*当我们把自动提交关闭,那sql就不是提交执行,于是我们一定要记住,当我们一个整体功能完成之后,自己要手动进行提交;
			--conn.commit但是失败之后,要记住数据回滚*/
			connection.rollback();
		} finally {
			ps2.close();
			ps.close();
			connection.close();
		}
	}

ACID (面试)
事务 : 一组操作 要么都成功 要么都失败
事务具有4个特征,分别是原子性、一致性、隔离性和持久性,简称事务的ACID特性;
原子性(atomicity) :一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作
一致性(consistency) : 一个事务执行前后,数据库都处于一致性状态
隔离性(isolation): 每一个事务都是单独的,事务和事务之间不影响
持久性(durability): 事务执行完了, 持久化到数据库

链接池

概念

在这里插入图片描述
你创建了一个池塘 池塘里面你放了很多链接 用完了就放回去 -->节省开关链接的时间

实现

在Java中,连接池使用javax.sql.DataSource接口来表示连接池,这里的DataSource就是连接池,连接池就是DataSource。
DataSource是接口,和JDBC一样,是Sun公司开发的一套接口,需要各大厂商实现;
需要导入相应包—导包…
所以使用连接池,首先需要导包;

常用的DataSource的实现有下面两种方式:
DBCP: Spring推荐的(Spring框架已经集成DBCP)
C3P0: Hibernate推荐的(早期)(Hibernate框架已经集成C3P0)持久层

DBCP连接池实现

1.导入jar包
commons-dbcp-1.3.jar,commons-pool-1.5.6.jar

2.代码
BasicDataSource就是DBCP的连接池实现

第一种配置方式
public class JDBCUtil {
	// 构造方法私有化
	private JDBCUtil() {
	};

	private static JDBCUtil instance;
	// 一启动就加载驱动 只执行一次
	static Properties ps = null;
	static BasicDataSource ds = null;
	static {
		ps = new Properties();
		try {
			ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
			ds = new BasicDataSource();
			ds.setDriverClassName(ps.getProperty("dirverClassName"));
			ds.setUsername(ps.getProperty("username"));
			ds.setPassword(ps.getProperty("password"));
			ds.setUrl(ps.getProperty("url"));
		} catch (IOException e) {
			e.printStackTrace();
		}

/*		 try {
		 		ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
		 		Class.forName(ps.getProperty("dirverClassName"));
		 } catch (Exception e) {
		 		e.printStackTrace();
		 }*/

		instance = new JDBCUtil();
	}

	public static JDBCUtil Instance() {
		return instance;
	}

	// 写加载数据库的驱动
	public Connection getconn() {
		Connection connection = null;
		try {
			//换成新的获取连接池的方式
			connection = ds.getConnection();
			// connection = DriverManager.getConnection(ps.getProperty("url"),
			// ps.getProperty("username"), ps.getProperty("password"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return connection;
	}

	// 关闭资源
	public void close(ResultSet rs, Statement State, Connection conn) {
		try {
			if (rs != null) {
				rs.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (State != null) {
					State.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				try {
					if (conn != null) {
						conn.close();
					}
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}
public class test {
	public static void main(String[] args) {
		Connection connection = JDBCUtil.Instance().getconn();
		try {
			String sql = "update bank set money=money-500 where name='过儿'";
			PreparedStatement ps = connection.prepareStatement(sql);
			ps.execute();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
第二种配置方式
public class JDBCUtil2 {
	// 构造方法私有化
	private JDBCUtil2() {
	};

	private static JDBCUtil2 instance;
	// 一启动就加载驱动 只执行一次
	static Properties ps = null;
	static DataSource ds = null;
	static {
		ps = new Properties();
		try {
			ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
			// 创建连接池
			ds = BasicDataSourceFactory.createDataSource(ps);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// try {
		// 		ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
		// 		Class.forName(ps.getProperty("dirverClassName"));
		// } catch (Exception e) {
		// 		e.printStackTrace();
		// }

		instance = new JDBCUtil2();
	}

	public static JDBCUtil2 Instance() {
		return instance;
	}

	// 写加载数据库的驱动
	public Connection getconn() {
		Connection connection = null;
		try {
			//换成新的获取连接池的方式
			connection = ds.getConnection();
			// connection = DriverManager.getConnection(ps.getProperty("url"),
			// ps.getProperty("username"), ps.getProperty("password"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return connection;
	}

	// 关闭资源
	public void close(ResultSet rs, Statement State, Connection conn) {
		try {
			if (rs != null) {
				rs.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (State != null) {
					State.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			} finally {
				try {
					if (conn != null) {
						conn.close();
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
public class test {
	public static void main(String[] args) {
		Connection connection = JDBCUtil2.Instance().getconn();
		try {
			String sql = "update bank set money=money-500 where name='过儿'";
			PreparedStatement ps = connection.prepareStatement(sql);
			ps.execute();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

返回主键

场景举例:先后添加product和product_stock时,需要拿到product插入时自增的id存到product_stock的product_id里

看文档做

StudentDaoImpl

	@Override
	public void insert(Student stu) {
		Connection conn = JDBCUtil.Instance().getconn();
		PreparedStatement ps = null;
		try {
			String sql = "insert into student(name,password) values(?,?)";
			ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			ps.setString(1, stu.getName());
			ps.setString(2, stu.getPassword());
			ps.executeUpdate();
			ResultSet rs = ps.getGeneratedKeys();
			while (rs.next()) {
				System.err.println(rs.getInt(1));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

JDBCTest

	@Test
	public void addStudent() throws Exception {
		StudentDaoImpl studentDaoImpl = new StudentDaoImpl();
		Student stu = new Student();
		stu.setName("小波波");
		stu.setPassword("857857958958");
		studentDaoImpl.insert(stu);
	}

BaseDao的抽取

BaseDao

public class BaseDao {
	public void excuteUpdate(String sql, Object... objects) {
		Connection conn = JDBCUtil2.Instance().getconn();
		PreparedStatement ps = null;
		try {
			ps = conn.prepareStatement(sql);
			for (int i = 0; i < objects.length; i++) {
				ps.setObject(i + 1, objects[i]);
			}
			ps.execute();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtil2.Instance().close(null, ps, conn);
		}
	}
}

实现类:

public class StudentDaoImpl extends BaseDao  implements IStudentDao{
    
    @Override
	public void insert(Student stu) {
		String  sql="insert into student(name,password) values(?,?)";
		excuteUpdate(sql, stu.getName(),stu.getPassword());
	}
	@Override
	public void update(Student stu) {
		String sql = "update student set name=?,password=? where id =?";
		excuteUpdate(sql, stu.getName(),stu.getPassword(),stu.getId());
	}
	@Override
	public void delete(Student stu) {
		String sql = "delete from student where id = ?";
		excuteUpdate(sql, stu.getId());
	}
}

JDBCTest

	@Test
	public void addStudent() throws Exception {
		StudentDaoImpl studentDaoImpl = new StudentDaoImpl();
		Student stu = new Student();
		stu.setName("小波波");
		stu.setPassword("857857");
		stu.setId(254172);
//		studentDaoImpl.insert(stu);
//		studentDaoImpl.delete(stu);
		studentDaoImpl.update(stu);
	}

http://www.niftyadmin.cn/n/5264055.html

相关文章

Kubectl Patch 的应用

报错 先前创建的crds不会删除。仍处于终止状态 [root@k8s-01 clickhouse-cluster]# kubectl apply -f sample05.yaml Error from server (MethodNotAllowed): error when creating "sample05.yaml": create not allowed while custom resource definition is term…

Tekton 基于 gitlab 触发流水线

Tekton 基于 gitlab 触发流水线 Tekton EventListener 在8080端口监听事件&#xff0c;Gitlab 提交代码产生push 事件&#xff0c;gitlab webhook触发tekton流水线执行。 前置要求&#xff1a; kubernetes集群中已部署 tekton pipeline、tekton triggers以及tekton dashboa…

螺丝厂家:5个自攻螺钉的优点

自钻螺钉的用途是什么&#xff1f; 自钻螺钉有钻头的尖端和安装时攻丝孔的锋利切割螺纹。自钻螺钉用于各种螺钉&#xff0c;用于快速钻入各种材料&#xff0c;无论是金属还是木材。自钻螺钉可以很容易地指出它们的槽或通常被称为槽。 自钻螺钉用于轻型应用。在安装过程中&…

docker-harbor仓库

Docker 镜像 容器 仓库 仓库&#xff1a;保存镜像 私有&#xff1a;自定义用户的形式登录仓库&#xff0c;拉取或者上传镜像&#xff08;内部管理的用户&#xff09; Harbor&#xff1a;是VMware公司开发的&#xff0c;开源的企业级的docker register项目 帮助用户快速的搭建…

TypeScript 实现扑克数据花色、数值获取和生成

1、TypeScript 实现扑克数据中获取花色和数值的功能的代码 type CardData number;const LOGIC_MASK_COLOR 0xF0; // 花色掩码 const LOGIC_MASK_VALUE 0x0F; // 数值掩码// 获取数值 function GetCardValue(cardData: CardData): number {return cardData & LOGIC_MASK…

Python接口自动化 —— Json 数据处理实战(详解)

简介   上一篇说了关于json数据处理&#xff0c;是为了断言方便&#xff0c;这篇就带各位小伙伴实战一下。首先捋一下思路&#xff0c;然后根据思路一步一步的去实现和实战&#xff0c;不要一开始就盲目的动手和无头苍蝇一样到处乱撞&#xff0c;撞得头破血流后而放弃了。不仅…

【PID学习笔记10】PID公式分析

写在前面 前面已经将控制系统的基础知识点过了一遍&#xff0c;从本节开始&#xff0c;将正式学习PID控制的相关知识&#xff0c;将会从基本的PID公式概念解释&#xff0c;再基于matlab仿真介绍十几种数字式PID的基本概念。本文重点讲解PID的经典公式。 一、连续与离散的概念…

麒麟V10 ARM内核aarch64 安装harbor redis 存储错误解决办法

安装harbor 里面自带的 redis 报错 “jemalloc: Unsupported system page size” 解决办法 下载指定镜像的redis 替换harbor 自带的redis镜像 修改 docker-compose 文件替换原来的镜像版本。 删除 重构镜像执行&#xff1a; docker-compose up –build 后台启动&#xff…