初识JDBC-篇二

一、DAO设计规范

1.编写DAO组件

  1. 定义DAO接口

  2. 编写对应DAO实现类

为什么我们需要定义接口?

首先我们要知道接口就是只给出了函数声明,但是是没有函数体类。函数体在实现类中给出(必须实现)。我们可以根据客户提出的需求,定义接口,业务具体实现是通过实现类来完成。当客户提出新的需求,只需要编写该业务逻辑新的实现类。这就是面向接口编程,面向接口编程有以下几个好处

  • 业务逻辑更加清晰

  • 增强代码的扩展性,可维护性

  • 接口和实现相分离,适合团队协作开发

  • 降低耦合度。便于以后升级扩展

一个例子:

假设有一个数据库DAO,突然根据需要,程序要连接两个数据库,一个Oracle,一个MySQL。我们便可以利用多态的思想定义以下接口和实现类。

2.各种命名规范

1.包名规范

我们平时写java代码包名一般都是 域名倒写.模块名称.组件名称 这种格式,同样DAO也是符合这种格式。我们通常会建立以下四个包:

  • package com.ThinMoon.jdbc.domain 存储所有的domain
  • package com.thinmoon.jdbc.dao 存储所有的dao接口
  • package com.thinmoon.jdbc.dao.impl 存储所有的Dao接口实现类
  • package com.thinmoon.jdbc.dao.test 存储Dao组件的测试类
2.类名规范
起名规范 存储位置
domain类 见名之意即可 存储在domain包中。用于描述一个对象,是一个javaBean
dao接口 IDomainDao
接口-domain-dao
存储在dao包中,用于表示某一个对象的CRUD声明
dao实现类 DomainDAOImpl
domain-dao-impl
存储到dao.impl包中,用于表示DAO接口的实现类,要实现DAO接口

一个栗子:

Dao接口类:IStudentDao

public interface IStudentDao {
	//1.保存数据
	void save(Student stu);
	//2.修改数据
	void update(Student stu);
	//3.删除数据
	void delete(int id);
	//4.获取指定学生
	Student get(int id);
	//5.获取所有学生
	List<Student> getAll();

}

Dao实现类:StudentDaoImpl

public class StudentDaoImpl implements IStudentDao {

	@Override
	public void save(Student stu) {
		Connection conn = null;
		PreparedStatement ps = null;

		try {
			// 1.加载驱动
			// 2.连接数据库
			conn = JDBCUtil.getConn();
			// 3.创建语句
			int id = stu.getId();
			int age = stu.getAge();
			String name = stu.getName();
			String sql = "insert into student(id, age, name)values(?, ?, ?)";
			// 4.执行语句
			ps = conn.prepareStatement(sql);
			ps.setInt(1, id);
			ps.setInt(2, age);
			ps.setString(3, name);
			int row = ps.executeUpdate();
			// 5.释放
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.释放
			JDBCUtil.close(conn, ps, null);

		}

	}

	@Override
	public void update(Student stu) {
		Connection conn = null;
		PreparedStatement ps = null;

		try {
			// 1.加载驱动
			// 2.连接数据库
			conn = JDBCUtil.getConn();
			// 3.创建语句
			int id = stu.getId();
			int age = stu.getAge();
			String name = stu.getName();
			String sql = "update student set name=?, age=? where id=?;";
			ps = conn.prepareStatement(sql);
			ps.setInt(3, id);
			ps.setInt(2, age);
			ps.setString(1, name);
			int row = ps.executeUpdate();
			// 5.释放
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.释放
			JDBCUtil.close(conn, ps, null);

		}
	}

	@Override
	public void delete(int id) {
		Connection conn = null;
		PreparedStatement ps = null;

		try {
			// 1.加载驱动
			// 2.连接数据库
			conn = JDBCUtil.getConn();
			// 3.创建语句
			String sql = "delete from student where id = ?;";
			// 4.执行语句
			ps = conn.prepareStatement(sql);
			ps.setInt(1, id);
			int row = ps.executeUpdate();
			// 5.释放
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.释放
			JDBCUtil.close(conn, ps, null);

		}

	}

	@Override
	public Student get(int id) {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet res = null;
		try {
			// 1.加载驱动
			// 2.连接数据库
			conn = JDBCUtil.getConn();
			// 3.创建语句
			String sql = "select * from student where id=?";
			// 4.执行语句
			ps = conn.prepareStatement(sql);
			ps.setInt(1, id);
			res = ps.executeQuery();
			if (res.next()) {
				Student stu = new Student();
				stu.setName(res.getString("name"));
				stu.setAge(res.getInt("age"));
				return stu;
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(conn, ps, res);
		}

		return null;
	}

	@Override
	public List<Student> getAll() {
		Connection conn = null;
		Statement st = null;
		ResultSet res = null;
		try {
			// 1.加载驱动
			// 2.连接数据库
			conn = JDBCUtil.getConn();
			// 3.创建语句
			String sql = "select * from student;";
			// 4.执行语句
			st = conn.createStatement();
			res = st.executeQuery(sql);
			List<Student> list = new ArrayList<Student>();
			while (res.next()) {
				Student stu = new Student();
				stu.setId(res.getInt("id"));
				stu.setName(res.getString("name"));
				stu.setAge(res.getInt("age"));
				list.add(stu);
			}
			return list;

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 5.释放
			JDBCUtil.close(conn, st, res);
		}

		return null;
	}

}

domain类:Student

public class Student {
	Integer id;
	Integer age;
	String name;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

工具类:JDBCUtil

public class JDBCUtil {
	public static String url = "jdbc:MySQL://localhost:3306/jdbc_db?serverTimezone=UTC&characterEncoding=utf-8";
	public static String user = "root";
	public static String pwd = "123456";
	public static String driver = "com.mysql.cj.jdbc.Driver";
	static {
		// 1.加载驱动
		try {
			Class.forName(JDBCUtil.driver);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	public static void close(Connection conn, Statement st, ResultSet res) {
		// 5.释放
		if (res != null) {
			try {
				res.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

		if (st != null) {
			try {

				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}

	public static Connection getConn() {

		try {
			// 2.连接数据库
			return DriverManager.getConnection(JDBCUtil.url, JDBCUtil.user, JDBCUtil.pwd);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}
}

测试类:StudentDaoTest

public class StudentDaoTest {

	@Test
	public void save() {
		Student stu = new Student();
		stu.setId(1);
		stu.setName("李白");
		stu.setAge(16);

		IStudentDao dao = new StudentDaoImpl();
		dao.save(stu);
	}

	@Test
	public void delete() {
		IStudentDao dao = new StudentDaoImpl();
		dao.delete(1);
	}

	@Test
	public void update() {
		Student stu = new Student();
		stu.setId(1);
		stu.setAge(16);
		stu.setName("老王");

		IStudentDao dao = new StudentDaoImpl();
		dao.update(stu);
	}

	@Test
	public void get() {
		IStudentDao dao = new StudentDaoImpl();
		Student stu = dao.get(1);
		if (stu != null) {
			System.out.println(stu.getName());
			System.out.println(stu.getAge());
		}else {
			System.out.println("null");
		}
	}
	@Test
	public void getAll() {
		IStudentDao dao = new StudentDaoImpl();
		List<Student> list = dao.getAll();
		
	}

}

二、SQL注入问题

从上文中我们可以发现sql语句和第一篇文章中好像不一样了,并且代码中Statement变成了PreparedStatement,接下来我们就开始介绍什么是PreparedStatement,以及什么是sql注入问题和PreparedStatement是怎么防止sql注入的。

1.什么是PreparedStatement

PreparedStatement是Statement的一个子接口,又叫做预编译语句。见名之意,就是我们可以预先写好sql语句然后再后续代码中给我们在预先写好的sql语句中的占位符赋值,这样就简化了我们书写sql语句时需要拼接的麻烦步骤。

// 3.创建语句
int id = stu.getId();
int age = stu.getAge();
String name = stu.getName();
String sql = "insert into student(id, age, name)values(?, ?, ?)";
// 4.执行语句
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
ps.setInt(2, age);
ps.setString(3, name);
int row = ps.executeUpdate();

类似上述代码般,我们可以给利用setXxx()给占位符赋值方便我们的书写。

2.什么是sql注入

在了解什么是sql注入前我们先看一个例子:

上面代码是一个简单的登录代码,通过和数据库对比用户名、密码返回是否登陆成功。可以看到我们上面还是使用之前sql语句需要自己进行拼接的情况,那么这种情况会出现什么问题呢?

在1中我们假设数据库中存在用户zs并且密码也是正确的,那么我们很自然的会登入成功这毋庸置疑。那么在2中呢?可以看到我们把2中的用户名写成" ' or 1=1 or ' "这种形式,那么当他与我们写的sql进行拼接时就会变成

select * from user where name = ' ' or 1=1 or ' ' and pwd = '12'

这样我们就得到一个返回值不为null的res了那么我们就可以成功进入系统了,这是一个极其危险的问题,我们把它称之为sql注入。

所以我们知道了什么是sql注入,SQL注入就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

3.PreparedStatement是怎么防止sql注入的?

我们将上面代码替换成PreparedStatement后再次用原来 " ' or 1=1 or ' "这种恶意代码去尝试登入会有什么结果呢?结果当然是登陆失败啦。当我们使用PreparedStatement时再用原来的恶意代码去尝试登陆,我们得到的拼接字符串是像下面代码一般:

select * from user where name = '\' or 1=1 or \' ' and pwd = '12'

可以看到恶意代码上的'都被加上了转移字符,这就是PreparedStatement能够防止大部分sql注入的原理。PreparedStatement会对非法字符加上转义字符后在进行拼接!

三、调用存储过程

调用存储过程需要用到CallableStatement,CallableStatement也是Statement的一个子接口。

1.调用无输出参数存储过程

public static void main(String[] args) throws Exception {
		Connection conn = JDBCUtil.getConn();
		//1.调用存储过程
		CallableStatement cs = conn.prepareCall("{ call getStu(?)}");
		//2.设置参数
		cs.setString(1, "老王");
		//3.执行存储过程
		ResultSet res = cs.executeQuery();
		if(res.next()) {
			Student stu = new Student();
			stu.setId(res.getInt("id"));
			stu.setAge(res.getInt("age"));
			stu.setName(res.getString("name"));
			System.out.println(stu);
		}
	}

2.调用有输出参数存储过程

public static void main(String[] args) throws Exception {
		Connection conn = JDBCUtil.getConn();
		//1.调用存储过程
		CallableStatement cs = conn.prepareCall("{ call getName(?,?)}");
		//2.设置参数
		cs.setInt(1, 1);
		cs.registerOutParameter(2, Types.VARCHAR);
		//3.执行存储过程
		cs.execute();
		
		String name = cs.getString(2);
		System.out.println(name);
		
	}
posted @ 2020-02-14 18:08  ThinMoon  阅读(134)  评论(0编辑  收藏  举报