先看昨天的数据库状态, 未操作数据库之前, 状态如下:
mysql> select * from student;
+-----------+-----------+------+------+-------+
| Sno | Sname | Ssex | sage | Sdept |
+-----------+-----------+------+------+-------+
| 200215121 | 李勇 | 男 | 18 | CS |
| 200215122 | 刘晨 | 女 | 19 | CS |
| 200215123 | 王敏 | 女 | 18 | MA |
| 200215125 | 张立 | 男 | 19 | IS |
| 200215126 | 张成民 | 男 | 18 | CS |
| 200215128 | 陈冬 | 男 | 18 | IS |
+-----------+-----------+------+------+-------+
6 rows in set (0.00 sec)
这是昨天写的直接操作数据库的代码:
点击(此处)折叠或打开
- package jdbc_test;
- import java.sql.*;
- public class jdbc_test {
- public static void main(String[] args) {
- System.out.println("haha");
- try {
- Class.forName("com.mysql.jdbc.Driver");
- Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/s_t", "root", "mikemike");
- Statement stmt = conn.createStatement();
- //ResultSet rs = stmt.executeQuery("select * from student where ssex='男'");
- int rs = stmt.executeUpdate("insert into student values('200210008','李莉','女',18,'CS');");
- stmt.close();
- conn.close();
- } catch (SQLException e) {
- System.out.println(e.getMessage());
- } catch (ClassNotFoundException e) {
- System.out.println("haha2");
- e.printStackTrace();
- }
- }
- }
mysql> select * from student;
+-----------+-----------+------+------+-------+
| Sno | Sname | Ssex | sage | Sdept |
+-----------+-----------+------+------+-------+
| 200210008 | 李莉 | 女 | 18 | CS |
| 200215121 | 李勇 | 男 | 18 | CS |
| 200215122 | 刘晨 | 女 | 19 | CS |
| 200215123 | 王敏 | 女 | 18 | MA |
| 200215125 | 张立 | 男 | 19 | IS |
| 200215126 | 张成民 | 男 | 18 | CS |
| 200215128 | 陈冬 | 男 | 18 | IS |
+-----------+-----------+------+------+-------+
7 rows in set (0.00 sec)
这里我们来看看代码中存在多少问题:
1) 添加删除不便, 如果我想要继续添加数据到数据库中, 就必须不断重复写如下sql语句, 这本身就是一个很low, 而且也不太现实的问题. 因为实际上我们的应用程序, 不可能每次因为要添加一个学生, 就不断的重新编译代码.
stmt.executeUpdate("insert into student values('******','*****','**',**,'**');");
2) 有可能会有脏数据, 甚至是sql语句的恶意代码, 如下, 假设应用程序中, 有人恶意传下来了这么一行sql的语句, 会导致什么情况:
stmt.execute("select * from student where sname = '李莉' and sno = '+varsno+'";);//其中+varsno+是一个变量
好了, 那现在, 这个坏人把+varson+这个变量赋值为中括号里面的内容[' or '1' = '1], 这句话会变成下面这样,
stmt.execute("select * from student where sname = '李莉' and sno = '' or '1' = '1' ";); //这就导致sno这个选择条件根本没用了.
更有甚者, 把+varson+这个变量赋值为[';drop table student;] //那我们的sql语句在执行完之后, 直接把表都给删了.
针对第2个问题, 我们在代码中, 采用Preparedstatment来代替Statement的实现, 具体用Preparedstatment代替Statement的好处自行百度好了.
对第1个问题, 我们来用DAO模式来实现我们的数据库连接.
先上代码:
点击(此处)折叠或打开
- package jdbc_test;
- import java.sql.*;
- class student { //这就是一个POJO, POJO实际上, 就是一个简单的只具有setter和getter方法的类.
- private String sno;
- private String sname;
- private String ssex;
- private int sage;
- private String sdept;
- public String getSno() {
- return sno;
- }
- public void setSno(String sno) {
- this.sno = sno;
- }
- public String getSname() {
- return sname;
- }
- public void setSname(String sname) {
- this.sname = sname;
- }
- public String getSsex() {
- return ssex;
- }
- public void setSsex(String ssex) {
- this.ssex = ssex;
- }
- public int getSage() {
- return sage;
- }
- public void setSage(int sage) {
- this.sage = sage;
- }
- public String getSdept() {
- return sdept;
- }
- public void setSdept(String sdept) {
- this.sdept = sdept;
- }
- }
- interface studentDAO {
- public int addStudent(student s);
- public int delStudent(student s);
- public int updateStudent(student s);
- public int queryStudent();
- }
- class studentDAOimpl implements studentDAO {
-
- Connection conn = null;
- studentDAOimpl(Connection conn)
- {
- this.conn = conn;
- }
-
- @Override
- public int addStudent(student s) {
- String addsql = "insert into student values(?,?,?,?,?);";
- try {
- PreparedStatement stmt = conn.prepareStatement(addsql);
- stmt.setString(1, s.getSno());
- stmt.setString(2, s.getSname());
- stmt.setString(3, s.getSsex());
- stmt.setInt(4, s.getSage());
- stmt.setString(5, s.getSdept());
- stmt.executeUpdate();
- stmt.close(); /*
- * 这里的close,我开始没加上.
- * 后来知道PreparedStatement申请的资源也是需要释放的.
- * 并且,PreparedStatement是Statement的 子接口,所以,
- * 我们直接在帮助文件里是看不到close方法的, 但事实上它继承了Statement的方法.
- * 这里也有个java的基础知识, 接口中定义的方法一般都是公共静态方法
- */
- } catch (SQLException e) {
- System.out.println(e.getMessage());
- e.printStackTrace();
- }
- return 0;
- }
- @Override
- public int delStudent(student s) {
- String delsql = "delete from student where sno=?;"; //实际上这里的删除语句,只实现了通过sno来删除数据库元组的功能,没有通过其它的来删除
- try {
- PreparedStatement stmt = conn.prepareStatement(delsql);
- stmt.setString(1, s.getSno());
- stmt.executeLargeUpdate();
-
- stmt.close(); //同add, 一样需要释放资源
- } catch (SQLException e) {
- System.out.println(e.getMessage());
- e.printStackTrace();
- }
- return 0;
- }
- @Override
- public int updateStudent(student s) {
- // TODO Auto-generated method stub
- return 0;
- }
- @Override
- public int queryStudent() {
- // TODO Auto-generated method stub
- return 0;
- }
- }
- public class jdbc_DAO {
- public static void main(String[] args) {
- Connection conn = null;
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (ClassNotFoundException e) {
- System.out.println(e.getMessage());
- e.printStackTrace();
- }
- try {
- conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/s_t", "root", "12345678");
- } catch (SQLException e) {
- System.out.println(e.getMessage());
- e.printStackTrace();
- }
- student s1 = new student();
- s1.setSno("200210002");
- s1.setSname("李莉");
- s1.setSdept("CS");
- s1.setSage(20);
- s1.setSsex("女");
- studentDAO daoimpl = new studentDAOimpl(conn);
- daoimpl.addStudent(s1);
- //daoimpl.delStudent(s1);
-
- try {
- conn.close(); //这里的资源一定也要记得, 需要释放
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- }
- }
解析: DAO: data access object, 用于对数据库访问的一个操作集合的对象.
1. 事实上, 对于DAO模式, 每一张表, 都可以定义一个自己的所谓domain, 实际上我理解的就是对每一个表, 都定义一个POJO, 也就是只有setter和getter函数的类, 用以作为用户对数据库表的元组赋值. 每一个属性都有一个setter和getter函数. 上一例中就是通过class student来实现的.
2. 对于每一个表的增删改查操作, 首先定义一个对应的DAO接口, 如本例中, student表就对应一个interface studentDAO, 而这个接口有下面的具体实现, class studentDAOimpl implements studentDAO ,所以事实上真正的增删改查操作, 都是在这个实现了接口的类class studentDAOimpl 中来完成的.
这样有什么好处? 增删改查的这些具体的操作的方法, 如果以后一旦发生了变化, 我们只需要修改对应的操作方法就可以了. main函数中对增删改查的调用本身并不需要做任何修改. 而再看本文最开始的那个例子, 假设某一天, 某个数据库的增加或删除语句发生了点变化, 那么我们的程序代码中, 凡是写了 stmt.execute 语句的地方, 都需要修改, 那可真是太费劲了.
上述代码的验证过程略过了, 是OK的.
我感觉, 这个DAO模式, 其实跟设计模式中的策略模式很有点像, 增删改查其实就是对数据student的各种算法策略, 把数据和策略分开来的一种思想.