Dao包结构图:
1.首先连接数据库
1 package com.util.db; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 import java.util.ResourceBundle; 9 10 import javax.management.loading.PrivateClassLoader; 11 12 import org.omg.CORBA.PRIVATE_MEMBER; 13 14 /** 15 * 连接数据库 16 * @author Administrator 17 * 18 */ 19 public class DBConn { 20 public static String URL; 21 public static String USERNAME; 22 public static String PASSWORD; 23 public static String DRIVER; 24 private static ResourceBundle resourceBundle = ResourceBundle.getBundle("com.util.db.db_config"); 25 private DBConn() {} 26 /** 27 *为连接数据库变量赋值 28 *加载jdbc驱动 29 */ 30 static{ 31 URL = resourceBundle.getString("jdbc.url"); 32 USERNAME = resourceBundle.getString("jdbc.username"); 33 PASSWORD = resourceBundle.getString("jdbc.password"); 34 DRIVER = resourceBundle.getString("jdbc.driver"); 35 try { 36 Class.forName(DRIVER); 37 } catch (ClassNotFoundException e) { 38 e.printStackTrace(); 39 } 40 } 41 /** 42 * 连接数据库,若连接失败则返回空 43 * @return 44 */ 45 public static Connection getConnection() { 46 Connection conn = null; 47 try { 48 conn = DriverManager.getConnection(URL, USERNAME,PASSWORD); 49 System.out.println("连接成功!"); 50 } catch (SQLException e) { 51 e.printStackTrace(); 52 System.out.println("连接失败"); 53 } 54 return conn; 55 } 56 /** 57 * 关闭数据库连接 58 * @param rs 59 * @param st 60 * @param conn 61 */ 62 public static void close(ResultSet rs,Statement st,Connection conn){ 63 try { 64 if (rs != null) {rs.close();} 65 if (st != null) {st.close();} 66 if (conn !=null) {conn.close();} 67 } catch (SQLException e) { 68 e.printStackTrace(); 69 } 70 71 } 72 public static void main(String[] args) { 73 System.out.println(URL); 74 System.out.println(USERNAME); 75 System.out.println(PASSWORD); 76 System.out.println(DRIVER); 77 78 } 79 }
连接所需配置文件如下:
jdbc.url = jdbc:mysql://localhost:3306/test?characterEncoding=utf8 jdbc.username = root jdbc.password = 000000 jdbc.driver =com.mysql.jdbc.Driver
2.对增删该查基本操作做定义 使用不定参数 来解决增删改的参数不同
1 package com.util.db; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 8 /** 9 * 为数据库操作提供方法模板 10 * @author Administrator 11 * 12 */ 13 public class JdbcTemplete { 14 /** 15 * 更新操作 16 * 返回 int 类型的操作行数 用作判断操作成功与否 17 * 若操作异常 则返回 -1 18 * @param sql 19 * @param args 20 * @return 21 */ 22 public int update(String sql,Object...args){ 23 Connection conn = null; 24 PreparedStatement ps = null; 25 conn = DBConn.getConnection(); 26 try { 27 ps = conn.prepareStatement(sql); 28 if(args!=null){ 29 for(int i = 0; i< args.length; i++){ 30 ps.setObject(i+1, args[i]); 31 } 32 } 33 34 int rs = ps.executeUpdate(); 35 System.out.println("操作成功!"); 36 return rs; 37 } catch (SQLException e) { 38 e.printStackTrace(); 39 // try { 40 // //事务回滚 41 // conn.rollback(); 42 // } catch (SQLException e1) {e1.printStackTrace();} 43 System.out.println("更新异常"); 44 return -1; 45 }finally{ 46 DBConn.close(null, ps, conn); 47 } 48 } 49 /** 50 * 查询操作 不做结果处理 51 * 结果处理放在 ResultSetHandler 52 * 由调用时编写匿名类 采用策略模式 使用户采用不同策略 处理不同对象 53 * @param sql 54 * @param handler 55 * @param args 56 * @return 57 */ 58 public Object query(String sql, ResultSetHandler handler, Object...args) { 59 Connection conn = null; 60 PreparedStatement ps = null; 61 ResultSet rs = null; 62 try { 63 conn = DBConn.getConnection(); 64 ps = conn.prepareStatement(sql); 65 if (args != null) { 66 for (int i = 0; i < args.length; i++) { 67 ps.setObject(i+1, args[i]); 68 } 69 } 70 rs = ps.executeQuery(); 71 return handler.doHandler(rs); 72 } catch (SQLException e) { 73 e.printStackTrace(); 74 return null; 75 }finally{ 76 DBConn.close(rs, ps, conn); 77 } 78 } 79 }
3.对操作查询结果作出接口声明 使用策略模式 对于不同的javaBean有不同的处理
package com.util.db; import java.sql.ResultSet; import java.sql.SQLException; /** * 操作连接数据库的结果集 * 声明接口可以对具体的实体做不同处理 * @author Administrator * */ public interface ResultSetHandler { public Object doHandler(ResultSet rs) throws SQLException; }
4.定义javaBean
1 package com.domain; 2 3 public class Person { 4 private int id; 5 private String name; 6 private int age; 7 private String description; 8 public Person() { 9 super(); 10 } 11 public Person(String name, int age, String description) { 12 super(); 13 this.name = name; 14 this.age = age; 15 this.description = description; 16 } 17 public Person(int id, String name, int age, String description) { 18 super(); 19 this.id = id; 20 this.name = name; 21 this.age = age; 22 this.description = description; 23 } 24 public int getId() { 25 return id; 26 } 27 public void setId(int id) { 28 this.id = id; 29 } 30 public String getName() { 31 return name; 32 } 33 public void setName(String name) { 34 this.name = name; 35 } 36 public int getAge() { 37 return age; 38 } 39 public void setAge(int age) { 40 this.age = age; 41 } 42 public String getDescription() { 43 return description; 44 } 45 public void setDescription(String description) { 46 this.description = description; 47 } 48 @Override 49 public String toString() { 50 return "Person [id=" + id + ", name=" + name + ", age=" + age 51 + ", description=" + description + "]"; 52 } 53 54 }
5. 定义Dao 对具体操作 声明接口
1 package com.dao; 2 3 import java.sql.SQLException; 4 import java.util.List; 5 6 import com.domain.Person; 7 8 /** 9 * 用户功能接口 10 * @author Administrator 11 * 12 */ 13 public interface PersonDao { 14 public void add(Person person)throws SQLException; 15 public void update(Person person)throws SQLException; 16 public void delete (int id)throws SQLException; 17 public Person findById(int id)throws SQLException; 18 public List findAll()throws SQLException; 19 20 21 }
6.对用户的操作进行实现
1 package com.dao.impl; 2 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.ArrayList; 6 import java.util.List; 7 8 import javax.management.j2ee.statistics.JDBCConnectionPoolStats; 9 10 import com.dao.PersonDao; 11 import com.domain.Person; 12 import com.util.db.JdbcTemplete; 13 import com.util.db.ResultSetHandler; 14 15 public class PersonDaoImpl implements PersonDao { 16 private JdbcTemplete jdbcTemplete; 17 public PersonDaoImpl(){ 18 jdbcTemplete = new JdbcTemplete(); 19 } 20 /** 21 * 添加 22 */ 23 @Override 24 public void add(Person person) throws SQLException { 25 String sql = "insert into person(name,age,description)value(?,?,?)"; 26 jdbcTemplete.update(sql, person.getName(),person.getAge(),person.getDescription()); 27 28 } 29 /** 30 * 更新操作 31 */ 32 @Override 33 public void update(Person person) throws SQLException { 34 String sql ="update person set name=?,age=?,description=? where id=?"; 35 jdbcTemplete.update(sql, person.getName(),person.getAge(),person.getDescription(),person.getId()); 36 } 37 38 @Override 39 public void delete(int id) throws SQLException { 40 String sql = "delete from person where id = ?"; 41 jdbcTemplete.update(sql,id); 42 } 43 44 @Override 45 public Person findById(final int id) throws SQLException { 46 String sql = "select name,age,description from person where id =?"; 47 return (Person) jdbcTemplete.query(sql, new ResultSetHandler() { 48 @Override 49 public Object doHandler(ResultSet rs) throws SQLException { 50 Person person = null; 51 if (rs.next()) { 52 person = new Person(); 53 person.setId(rs.getInt(id)); 54 person.setName(rs.getString(1)); 55 person.setAge(rs.getInt(2)); 56 person.setDescription(rs.getString(3)); 57 } 58 return person; 59 } 60 },id ); 61 62 } 63 64 @Override 65 public List findAll() throws SQLException { 66 String sql = "select id,name,age,description from person"; 67 return (List) jdbcTemplete.query(sql,new ResultSetHandler() { 68 @Override 69 public Object doHandler(ResultSet rs) throws SQLException { 70 List<Person> personList = new ArrayList<>(); 71 Person person =null; 72 while (rs.next()) { 73 person = new Person(); 74 person.setId(rs.getInt(1)); 75 person.setName(rs.getString(2)); 76 person.setAge(rs.getInt(3)); 77 person.setDescription(rs.getString(4)); 78 personList.add(person); 79 } 80 return personList; 81 } 82 }); 83 84 } 85 86 }
7.编写测试类
1 package com.test; 2 3 import java.sql.SQLException; 4 import java.util.List; 5 6 import com.dao.PersonDao; 7 import com.dao.impl.PersonDaoImpl; 8 import com.domain.Person; 9 10 public class Test { 11 /** 12 * 13 * @param args 14 */ 15 public static void main(String[] args) { 16 PersonDao personDao = new PersonDaoImpl(); 17 18 try { 19 20 // personDao.add(new Person("CCC", 13, "CCC")); 21 22 personDao.delete(3); 23 // 24 // personDao.update(new Person(3, "DDD", 10, "DDD")); 25 // 26 // 27 // Person p4 = personDao.findById(2); 28 // System.out.println(p4.toString()); 29 // 30 // 31 // List<Person> personList = personDao.findAll(); 32 // for (Person p : personList) { 33 // System.out.println(p.toString()); 34 // } 35 36 } catch (SQLException e) { 37 e.printStackTrace(); 38 } 39 } 40 }
1.Statement、PreparedStatement和CallableStatement都是接口(interface)。
2.Statement继承自Wrapper、PreparedStatement继承自Statement、CallableStatement继承自PreparedStatement。
3.
a.Statement:
普通的不带参的查询SQL;支持批量更新,批量删除;
b.PreparedStatement:
可变参数的SQL,支持占位符,每个占位符可占一个位,
2.Statement继承自Wrapper、PreparedStatement继承自Statement、CallableStatement继承自PreparedStatement。
3.
a.Statement:
普通的不带参的查询SQL;支持批量更新,批量删除;
b.PreparedStatement:
可变参数的SQL,支持占位符,每个占位符可占一个位,
编译一次,执行多次,效率高;
安全性好,有效防止Sql注入等问题;
支持批量更新,批量删除;
c.CallableStatement:
继承自PreparedStatement,支持带参数的SQL操作;
支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;
安全性好,有效防止Sql注入等问题;
支持批量更新,批量删除;
c.CallableStatement:
继承自PreparedStatement,支持带参数的SQL操作;
支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;
PreparedStatement是预编译的,使用PreparedStatement有几个好处
1. 在执行可变参数的一条SQL时,PreparedStatement比Statement的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高。
2. 安全性好,有效防止Sql注入等问题。
3. 对于多次重复执行的语句,使用PreparedStament效率会更高一点,并且在这种情况下也比较适合使用batch;
4. 代码的可读性和可维护性。
如何防止注入攻击:
如果你是做Java web应用开发的,那么必须熟悉那声名狼藉的SQL注入式攻击。去年Sony就遭受了SQL注入攻击,被盗用了一些Sony play station(PS机)用户的数据。在SQL注入攻击里,恶意用户通过SQL元数据绑定输入,比如:某个网站的登录验证SQL查询代码为:
strSQL = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"
恶意填入:
userName = "1' OR '1'='1";
passWord = "1' OR '1'='1"
那么最终SQL语句变成了:
strSQL = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"
因为WHERE条件恒为真,这就相当于执行:
strSQL = "SELECT * FROM users;"
因此可以达到无账号密码亦可登录网站。如果恶意用户要是更坏一点,再在后面加一句 :
drop table users;
SQL语句变成了:
strSQL = "SELECT * FROM users;drop table users;
这样一来,虽然没有登录,但是数据表都被删除了。
然而使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,参数只是用作参数,而不会作为指令拼成SQL语句,因此就算参数中含有破坏性的指令,也不会被数据库所运行。
补充:避免SQL注入的第二种方式:
在组合SQL字符串的时候,先对所传入的参数做字符取代(将单引号字符取代为连续2个单引号字符,因为连续2个单引号字符在SQL数据库中会视为字符中的一个单引号字符,譬如:
strSQL = "SELECT * FROM users WHERE name = '" + userName + "';"
传入字符串:
userName = " 1' OR 1=1 "
把userName做字符替换后变成:
userName = " 1'' OR 1=1"
最后生成的SQL查询语句为:
strSQL = "SELECT * FROM users WHERE name = '1'' OR 1=1'
这样数据库就会去系统查找name为“ 1′ OR 1=1 ”的记录,而避免了SQL注入。
CallableStatement使用:
public class Conn { public static String URL; public static String USERNAME; public static String PASSWORD; public static String DRIVER; private static ResourceBundle resourceBundle = ResourceBundle.getBundle("com.util.db.db_config"); private void Conn() {} static{ URL = resourceBundle.getString("jdbc.url"); USERNAME = resourceBundle.getString("jdbc.username"); PASSWORD = resourceBundle.getString("jdbc.password"); DRIVER = resourceBundle.getString("jdbc.driver"); try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 连接数据库,若连接失败则返回空 * @return */ public static Connection getConnection() { Connection conn = null; try { conn = DriverManager.getConnection(URL, USERNAME,PASSWORD); System.out.println("连接成功!"); } catch (SQLException e) { e.printStackTrace(); System.out.println("连接失败"); } return conn; } /** * 关闭数据库连接 * @param rs * @param st * @param conn */ public static void close(ResultSet rs,CallableStatement cstmt,Connection conn){ try { if (rs != null) {rs.close();} if (cstmt != null) {cstmt.close();} if (conn !=null) {conn.close();} } catch (SQLException e) { e.printStackTrace(); } } /** * 1、执行不带参数但有返回值的存储过程 */ static public int getUserCount() { Connection conn = Conn.getConnection(); CallableStatement cstmt = null; int rowsCount = 0; try { String sql = "{call getUserCount(?)}"; //创建CallableStatement 对象 cstmt = conn.prepareCall(sql); //注册输出参数 cstmt.registerOutParameter(1,Types.INTEGER); //执行存储过程 cstmt.execute(); rowsCount = cstmt.getInt(1); } catch (SQLException e) { System.out.println("执行失败!"); e.printStackTrace(); }finally { Conn.close(null,cstmt, conn); return rowsCount; } } /** * 2、执行带参数带返回值的存储过程 */ static public int getUserCountCondit(String username) { Connection conn = Conn.getConnection(); CallableStatement cstmt = null; int rowsCount = 0; try { String sql = "{call proc_getUserCountCondit(?,?)}"; //创建CallableStatement 对象 cstmt = conn.prepareCall(sql); //输入参数赋值 cstmt.setString(1,username); //注册输出参数 cstmt.registerOutParameter(1,Types.INTEGER); //执行存储过程 cstmt.execute(); rowsCount = cstmt.getInt(1); } catch (SQLException e) { System.out.println("执行失败!"); e.printStackTrace(); }finally { Conn.close(null,cstmt, conn); return rowsCount; } } /** * 3、执行返回值为游标的存储过程 */ static public List<User> getUserListByProc(){ Connection conn = Conn.getConnection(); CallableStatement cstmt = null; ResultSet rs = null; List<User> userList = new ArrayList(); try { String sql = "{? = call FUNC_GETUSERNAME(?)}"; //创建CallableStatement 对象 cstmt = conn.prepareCall(sql); //注册输出参数 cstmt.registerOutParameter(1,Types.REF_CURSOR); //执行存储过程 cstmt.execute(); rs = cstmt.getResultSet(); while (rs.next()) { String username = rs.getString("username"); String password = rs.getString("password"); userList.add(new User(username, password)); } } catch (SQLException e) { System.out.println("执行失败!"); e.printStackTrace(); }finally { Conn.close(rs,cstmt, conn); return userList; } } public static void main(String[] args) { } static class User{ String username = null; String password = null; public User(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } }