JDBCUtils工具类的封装
package cn.itcast.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ResourceBundle; /** * JDBC 工具类,抽取公共方法 * * @author seawind * */ public class JDBCUtils { private static final String DRIVERCLASS; private static final String URL; private static final String USER; private static final String PWD; static { ResourceBundle bundle = ResourceBundle.getBundle("dbconfig"); DRIVERCLASS = bundle.getString("DRIVERCLASS"); URL = bundle.getString("URL"); USER = bundle.getString("USER"); PWD = bundle.getString("PWD"); } // 建立连接 public static Connection getConnection() throws Exception { loadDriver(); return DriverManager.getConnection(URL, USER, PWD); } // 装载驱动 private static void loadDriver() throws ClassNotFoundException { Class.forName(DRIVERCLASS); } // 释放资源 public static void release(ResultSet rs, Statement stmt, Connection conn) { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } release(stmt, conn); } public static void release(Statement stmt, Connection conn) { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } }
JDBC增删改查
package cn.itcast.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.junit.Test; /** * 对users表增删改查 * * @author seawind * */ public class JDBCCURD { // 查询 @Test public void demo4() throws Exception { // 查询返回结果集 Connection conn = null; Statement stmt = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); String sql = "select * from users"; rs = stmt.executeQuery(sql); while (rs.next()) { System.out.println(rs.getString("name")); } } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(rs, stmt, conn); } } // 删除users 中 bbb @Test public void demo3() throws Exception { Connection conn = null; Statement stmt = null; try { // 建立连接 conn = JDBCUtils.getConnection(); // 操作数据库 stmt = conn.createStatement(); String sql = "delete from users where name = 'bbb'"; int row = stmt.executeUpdate(sql); System.out.println(row); // 如果row 为0 失败,不为0 成功 } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(stmt, conn); } } // 修改users表数据 @Test public void demo2() throws Exception { Connection conn = null; Statement stmt = null; try { // 建立连接 conn = JDBCUtils.getConnection(); // 操作数据库 stmt = conn.createStatement(); String sql = "update users set email = 'service@itcast.cn' where name='eee'"; int row = stmt.executeUpdate(sql); System.out.println(row); // 如果row 为0 失败,不为0 成功 } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(stmt, conn); } } // 向users表插入一条数据 @Test public void demo1() throws Exception { Connection conn = null; Statement stmt = null; try { // 建立连接 conn = JDBCUtils.getConnection(); // 操作数据 stmt = conn.createStatement(); String sql = "insert into users values(5,'eee','1234','eee@itcast.cn')"; int row = stmt.executeUpdate(sql); System.out.println(row);// 插入成功 1 失败 0 } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.release(stmt, conn); } } }
JAVAEE模式之DAO模式
JavaEE软件体系结构---架构模式
客户端层 实现技术 DHTML(HTML+CSS+JavaScript) JavaApplication
web层 Servlet JSP
业务层 EJB(体积庞大使用复杂中小型软件不使用,只是使用JavaBean代替但是在大型软件还是需要使用的)
持久层(数据访问层) JDBC接口规范
服务器端层因为被分为三部分也被称为经典三层架构,三层模式,每一层都有着严格的分工,web层接收请求生成响应,业务层编写程序业务处理流程,持久层数据库的增删改查。
为了简化开发流程相对于每个层次在企业开发中都封装了对应的各层框架,SSH(Struts+Spring+Hibernate)
DAO模式 Data Acess Object
是数据持久层的设计模式,不同于23种设计模式,JavaEE设计模式是围绕着服务器三层结构设计的。
MVC设计模式与JavaEE经典三层架构并没有直接关系,是两套不同的划分体系。
web层的Servlet对应着MVC中的Controller,web层的JSP对应着View,业务层与持久层对应着Model
DAO模式封装了对于数据源的操作,数据源可能是文件,数据库等任意存储方式,负责管理与数据源的连接,负责数据的存取(CRUD)
通过DAO模式对底层数据源封装的实现,业务层操作数据层时不需要知道数据层底层的具体存储实现,直接通过对DAO封装对象进行操作来实现对数据层底层数据源的操作。
DAO模式的好处,业务层的开发不需要关注数据底层实现,简化了业务层的开发,对过对象操作完成数据层数据增删改查。
DAO常用对象
BussinessObject 代表数据的使用者(业务层程序)
DataAcessObject;抽象封装了对底层数据源的操作(数据层程序)
DataSource 数据源(数据库或文件)
TransferObject 表示数据的JavaBean
业务层对象BussinessObject通过将数据对象transferObject传递给数据层对象DataAcessObject完成对数据源对象DataSource的增删改查
DAO模式实例,用户user表的增删改查。
设计分析
DAO模式去设计程序时,就是对程序进行分层,对象封装。
这里分为两层,一是业务层,一是数据层
1,首先新建user类,封装数据的Javabean,即TransferObject对象。
public class User { // 类的属性 应该与数据库字段 一一对应 private int id; private String name; private String pwd; private String email; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
2,数据层,UserDAO类的建立,封装数据库的增删改查方法,方法参数为user对象,为了简化开发需要JDBCUtils类
public class UserDAO { public User login(User user) { // JDBC查询 User existUser = null; Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); String sql = "select * from users where name = ? and pwd = ?"; // 数据库编译时 stmt = conn.prepareStatement(sql); // 将sql 发送给数据库进行编译 // 设置参数 stmt.setString(1, user.getName()); // or -- 传入数据值,不会作为关键字 --防止注入 stmt.setString(2, user.getPwd()); // 因为之前 将sql 传递数据库 rs = stmt.executeQuery(); // 如果登陆成功 只有一条记录 if (rs.next()) { existUser = new User(); existUser.setId(rs.getInt("id")); existUser.setName(rs.getString("name")); existUser.setPwd(rs.getString("pwd")); existUser.setEmail(rs.getString("email")); } } catch (Exception e) { e.printStackTrace(); } return existUser; } public User findById(int id) { User user = null; // 查询数据 Connection conn = null; Statement stmt = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); String sql = "select * from users where id = " + id; rs = stmt.executeQuery(sql); // 结果集只有一条 if (rs.next()) { // 结果集每行 一条记录 ----- User对象 user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setPwd(rs.getString("pwd")); user.setEmail(rs.getString("email")); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.release(rs, stmt, conn); } return user; } public List<User> findAll() { List<User> users = new ArrayList<User>(); // 查询数据 Connection conn = null; Statement stmt = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); String sql = "select * from users"; rs = stmt.executeQuery(sql); while (rs.next()) { // 结果集每行 一条记录 ----- User对象 User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setPwd(rs.getString("pwd")); user.setEmail(rs.getString("email")); users.add(user); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.release(rs, stmt, conn); } return users; } // 全字段修改 public void update(User user) { Connection conn = null; Statement stmt = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); // 参数在传入user对象中 // 在修改时,通过id修改该条记录所有字段 String sql = "update users set name='" + user.getName() + "' ,pwd='" + user.getPwd() + "',email='" + user.getEmail() + "' where id = " + user.getId(); stmt.executeUpdate(sql); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.release(stmt, conn); } } public void delete(User user) { Connection conn = null; Statement stmt = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); // 参数在传入user对象中 String sql = "delete from users where id = " + user.getId(); stmt.executeUpdate(sql); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.release(stmt, conn); } } // 封装了数据源 增删改查底层实现 public void insert(User user) { // 将 user中数据 保存到数据库 Connection conn = null; Statement stmt = null; try { conn = JDBCUtils.getConnection(); stmt = conn.createStatement(); // 参数在传入user对象中 String sql = "insert into users values(" + user.getId() + ",'" + user.getName() + "','" + user.getPwd() + "','" + user.getEmail() + "')"; stmt.executeUpdate(sql); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.release(stmt, conn); } } }
3,业务层,封装对数据对象的操作,将数据对象传递给数据层响应方法。
public class UserService { /** * 根据用户名 和 密码查询其它信息 * * @param user * @return 如果返回null证明 用户名或者密码错误 */ public User login(User user) { // 查询 将 user对象传递 DAO UserDAO userDAO = new UserDAO(); return userDAO.login(user); } @Test public void testFindById() { int id = 3; UserDAO userDAO = new UserDAO(); User user = userDAO.findById(id); System.out.println(user.getName()); } @Test public void testFindAll() { UserDAO userDAO = new UserDAO(); List<User> users = userDAO.findAll(); for (User user : users) { System.out.println(user.getName()); } } @Test public void testUpdate() { User user = new User(); user.setId(3); user.setName("大国"); user.setPwd("123456"); user.setEmail("daguo@itcast.cn"); UserDAO userDAO = new UserDAO(); userDAO.update(user); } @Test public void testDelete() { User user = new User(); user.setId(10); UserDAO userDAO = new UserDAO(); userDAO.delete(user); } @Test public void testInsert() { User user = new User(); user.setId(10); user.setName("小王"); user.setPwd("123"); user.setEmail("xiaowang@itcast.cn"); // 业务层将 对象 传递 数据层DAO 完成对数据表增删改查 UserDAO userDAO = new UserDAO(); userDAO.insert(user); } }
/** * web层登陆 * * @author seawind * */ public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获得form表单对象 封装JavaBean request.setCharacterEncoding("utf-8"); String name = request.getParameter("name"); String pwd = request.getParameter("pwd"); User user = new User(); user.setName(name); user.setPwd(pwd); // 传递 JavaBean 给 业务层 UserService userService = new UserService(); User existUser = userService.login(user); // 根据结果决定跳转页面 if (existUser == null) { // 登陆失败 request.setAttribute("msg", "用户名或者密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); return; } else { // 登陆成功 request.getSession().setAttribute("existUser", existUser); request.getRequestDispatcher("/welcome.jsp").forward(request, response); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
SQL注入
在form表单提交信息时根据SOL语法的特殊性,编写关键字或者使用注释注入,而这些关键字拼接到sql中可能会改变原先sql运行的效果,从而达到攻击的目的
如底层登录查询时执行的语句String sql="select * from users where name='"+user.getName()+"'and password='"+user.getPassword()+"';"
此处只要将user.getName()的值中加入or关键字,使select语句判断忽略密码一定会执行,即能登录成功。
在java中要防止SQL注入,使用PrepareStatement预编译接口,该接口的查询过程是将查询语句与参数的结合分成两个过程,sql语句先经数据库编译,使用?占位参数,然后再将参数传入,查询最终结果。
String sql = "select * from users where name = ? and pwd = ?"; // 数据库编译时
stmt = conn.prepareStatement(sql); // 将sql 发送给数据库进行编译
// 设置参数
stmt.setString(1, user.getName()); // or -- 传入数据值,不会作为关键字 --防止注入
stmt.setString(2, user.getPwd());
// 因为之前 将sql 传递数据库
rs = stmt.executeQuery();
JDBC大数据的处理
在实际开发中,程序需要把大文本 Text 或二进制数据 Blob保存到数据库。
Text是mysql叫法,Oracle中叫Clob
基本概念:大数据也称之为LOB(Large Objects),LOB又分为:
clob和blob
clob用于存储大文本。Text
blob用于存储二进制数据,例如图像、声音、二进制文等。
对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text
Text和blob分别又分为:
TINYTEXT(255)、TEXT(64k)、MEDIUMTEXT(16M)和LONGTEXT(4G)
TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
对于MySQL中的Text类型,可调用如下方法设置:
PreparedStatement.setCharacterStream(index, reader, length);
//注意length长度须设置,并且设置为int型
//当包过大时修改服务器my.ini配置:[mysqld] max_allowed_packet=64M
对MySQL中的Text类型,可调用如下方法获取:
reader = resultSet. getCharacterStream(i);
在实际开发中很少需要将大文件保存在数据库中
JDBC批处理操作
当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率,注意批处理是不支持查询的
实现批处理的方式有两种
1,使用statement接口的批处理
Statement.addBatch(sql) ;将一批sql语句放入缓存
执行批处理SQL语句
executeBatch()方法:执行批处理命令
clearBatch()方法:清除批处理命令
实例
Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtil.getConnection(); String sql1 = "insert into person(name,password,email,birthday) values('kkk','123','abc@sina.com','1978-08-08')"; String sql2 = "update user set password='123456' where id=3"; st = conn.createStatement(); st.addBatch(sql1); //把SQL语句加入到批命令中 st.addBatch(sql2); //把SQL语句加入到批命令中 st.executeBatch(); } finally{ JdbcUtil.free(conn, st, rs); }
优点:可以向数据库发送多条不同的SQL语句。
缺点:
SQL语句没有预编译。
当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。
例如:
Insert into user(name,password) values(‘aa’,’111’);
Insert into user(name,password) values(‘bb’,’222’);
Insert into user(name,password) values(‘cc’,’333’);
Insert into user(name,password) values(‘dd’,’444’);
实现批处理的第二种方式:
PreparedStatement.addBatch()
实例
conn = JdbcUtil.getConnection(); String sql = "insert into person(name,password,email,birthday) values(?,?,?,?)"; st = conn.prepareStatement(sql); for(int i=0;i<50000;i++){ st.setString(1, "aaa" + i); st.setString(2, "123" + i); st.setString(3, "aaa" + i + "@sina.com"); st.setDate(4,new Date(1980, 10, 10)); st.addBatch(); if(i%1000==0){ st.executeBatch(); st.clearBatch(); } } st.executeBatch();
采用PreparedStatement.addBatch()实现批处理
优点:发送的是预编译后的SQL语句,执行效率高。
缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。