废话不说,先来张包结构的图片:
很明显,采用了经典MVC三层架构。除了架构所需的包以外,还添加了一个工具包tool,里面有MD5加密和生成验证码的类,具体代码如下:
1 package com.jiy.JcMiniSNS.tool; 2 3 import java.security.MessageDigest; 4 5 /** 6 * MD5工具类 7 * @author 来自网络 8 * 9 */ 10 public class MD5Tool { 11 12 //十六进制下数字到字符的映射数组 13 private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; 14 15 /** 对字符串进行MD5加密 */ 16 private static String encodeByMD5(String originString){ 17 if (originString != null){ 18 try{ 19 //创建具有指定算法名称的信息摘要 20 MessageDigest md = MessageDigest.getInstance("MD5"); 21 //使用指定的字节数组对摘要进行最后更新,然后完成摘要计算 22 byte[] results = md.digest(originString.getBytes()); 23 //将得到的字节数组变成字符串返回 24 String resultString = byteArrayToHexString(results); 25 //return resultString.toUpperCase(); 26 return resultString; 27 } catch(Exception ex){ 28 ex.printStackTrace(); 29 } 30 } 31 return null; 32 } 33 34 /** * 把inputString加密 */ 35 public static String generatePassword(String inputString) { 36 return encodeByMD5(inputString); 37 } 38 39 /** 40 * 验证输入的密码是否正确 41 * @param password 加密后的密码 42 * @param inputString 输入的字符串 43 * @return 验证结果,TRUE:正确 FALSE:错误 44 */ 45 public static boolean validatePassword(String password, String inputString) { 46 if (password.equals(encodeByMD5(inputString))) { 47 return true; 48 } else { 49 return false; 50 } 51 } 52 53 /** 54 * 转换字节数组为十六进制字符串 55 * @param 字节数组 56 * @return 十六进制字符串 57 */ 58 private static String byteArrayToHexString(byte[] b){ 59 StringBuffer resultSb = new StringBuffer(); 60 for (int i = 0; i < b.length; i++){ 61 resultSb.append(byteToHexString(b[i])); 62 } 63 return resultSb.toString(); 64 } 65 66 /** 将一个字节转化成十六进制形式的字符串 */ 67 private static String byteToHexString(byte b){ 68 int n = b; 69 if (n < 0) 70 n = 256 + n; 71 int d1 = n / 16; 72 int d2 = n % 16; 73 return hexDigits[d1] + hexDigits[d2]; 74 } 75 }
1 package com.jiy.JcMiniSNS.tool; 2 3 import java.awt.Color; 4 import java.awt.Font; 5 import java.awt.Graphics; 6 import java.awt.image.BufferedImage; 7 import java.io.IOException; 8 import java.util.Random; 9 10 import javax.imageio.ImageIO; 11 import javax.servlet.http.HttpServletRequest; 12 import javax.servlet.http.HttpServletResponse; 13 14 /** 15 * 验证码生成类 16 * @author jiy 17 * 18 */ 19 public class VerificationCode { 20 21 private static final int WIDTH=60; 22 private static final int HEIGHT=30; 23 24 private BufferedImage img; 25 private Graphics g; 26 27 public VerificationCode(HttpServletRequest request,HttpServletResponse response) throws IOException 28 { 29 init(); 30 drawCode(request); 31 drawLine(); 32 ImageIO.write(img, "gif", response.getOutputStream()); 33 } 34 35 private void init() 36 { 37 img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); 38 g = img.getGraphics(); 39 g.setColor(Color.WHITE); 40 g.fillRect(0, 0, WIDTH, HEIGHT); 41 g.setColor(Color.RED); 42 g.drawRect(0, 0, WIDTH-1, HEIGHT-1); 43 } 44 45 /** 46 * 画干扰线条 47 */ 48 private void drawLine() 49 { 50 g.setColor(Color.BLUE); 51 Random r = new Random(); 52 for(int i=0; i<3; i++){ 53 g.drawLine(r.nextInt(WIDTH), r.nextInt(HEIGHT), r.nextInt(WIDTH), r.nextInt(HEIGHT)); 54 } 55 } 56 57 /** 58 * 画验证码并存入session 59 * @param request 60 */ 61 private void drawCode(HttpServletRequest request) 62 { 63 Random r = new Random(); 64 String checkCode = r.nextInt(9000)+1000+"";//1000-9999 65 g.setColor(Color.red); 66 g.setFont(new Font("微软雅黑",2,16)); 67 g.drawString(checkCode, 10, 20); 68 //在Session中添加属性"checkCode"=验证码 69 request.getSession().setAttribute("checkCode", checkCode); 70 //System.out.println(checkCode); 71 } 72 }
在程序开发的过程中,我发现了以前经常用的BaseDao的不合理的地方:为了更好地封装数据库操作,大家通常会在BaseDao里面写一个叫做getQuery(String sql,Object...paras)的方法,并且把常用的Connection、PreparedStatement、ResultSet作为BaseDao的成员,然后让dao层的代码继承BaseDao。这样的做法明显提高了dao层代码的开发效率,但我发现在实际运用的时候出现了问题:
1 /** 2 * 执行查询操作 3 * @param sql sql语句 4 * @param paras 参数数组 5 * @return 查询结果的ResultSet对象 6 */ 7 public ResultSet getQuery(String sql,Object...paras) 8 { 9 try { 10 pstmt = conn.prepareStatement(sql); 11 if(paras != null && paras.length != 0) 12 { 13 for(int i = 0;i < paras.length;i++) 14 { 15 pstmt.setObject(i+1, paras[i]); 16 } 17 } 18 res = pstmt.executeQuery(); 19 } catch (SQLException e) { 20 throw new RuntimeException("执行查询失败!"+e.getMessage()); 21 } 22 return res; 23 }
如果在动态消息管理的实现类里面有一个找动态的方法叫做findDynamics()调用了getQuery(String sql,Object...paras)方法,同时在同一个类中也有一个叫做findTells()用于寻找相关动态的评论的方法调用了getQuery(String sql,Object...paras)这个方法,在findDynamics()遍历所查询到的ResultSet用于初始化相关对象的时候调用了findTells()方法,findTells()方法在调用getQuery(String sql,Object...paras)的过程中执行到了 res = pstmt.executeQuery(); 这行代码,由于是在同一个类中,所以他们的res对象是共享的,于是在findDynamics()遍历res对象第一遍的时候,res已经被重新赋值了。这样就导致无论怎么样只能得到一条dynamics的记录。
所以,getQuery(String sql,Object...paras)方法中最好不要使用成员变量中的结果集。改进后完整的BaseDao类如下:
1 package com.jiy.JcMiniSNS.dao; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 import java.sql.SQLException; 8 9 10 /** 11 * JDBC连接基本类 12 * @author jiy 13 * 14 */ 15 public class BaseDao { 16 17 18 // private static final String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; 19 // private static final String URL = "jdbc:sqlserver://localhost:1433;DatabaseName=db_miniSNS"; 20 21 private static final String DRIVER = "com.mysql.jdbc.Driver"; 22 private static final String URL = "jdbc:mysql://localhost:3306/db_minisns"; 23 private static final String USER = "root"; 24 private static final String PWD = "root"; 25 26 public Connection conn = null; 27 public PreparedStatement pstmt = null; 28 public ResultSet res = null; 29 30 static{ 31 try { 32 Class.forName(DRIVER); 33 } catch (ClassNotFoundException e) { 34 throw new RuntimeException("数据库驱动加载失败!"); 35 } 36 } 37 38 /** 39 * 获取jdbc连接 40 */ 41 public void getConnection() 42 { 43 try { 44 conn = DriverManager.getConnection(URL,USER,PWD); 45 46 } catch (SQLException e) { 47 throw new RuntimeException("获取数据库连接失败!" + e.getMessage()); 48 } 49 } 50 51 /** 52 * 执行查询操作 53 * @param sql sql语句 54 * @param paras 参数数组 55 * @return 查询结果的ResultSet对象 56 */ 57 public ResultSet getQuery(String sql,Object...paras) 58 { 59 ResultSet queryRes = null; 60 try { 61 pstmt = conn.prepareStatement(sql); 62 if(paras != null && paras.length != 0) 63 { 64 for(int i = 0;i < paras.length;i++) 65 { 66 pstmt.setObject(i+1, paras[i]); 67 } 68 } 69 queryRes = pstmt.executeQuery(); 70 } catch (SQLException e) { 71 throw new RuntimeException("执行查询失败!"+e.getMessage()); 72 } 73 return queryRes; 74 } 75 76 77 /** 78 * 执行更新操作 79 * @param sql sql语句 80 * @param paras 参数列表 81 * @return 受影响的行数 82 */ 83 public int getUpdate(String sql,Object...paras) 84 { 85 int result = 0; 86 try { 87 pstmt = conn.prepareStatement(sql); 88 if(paras != null && paras.length != 0) 89 { 90 for(int i = 0;i < paras.length;i++) 91 { 92 pstmt.setObject(i+1, paras[i]); 93 } 94 } 95 result = pstmt.executeUpdate(); 96 } catch (SQLException e) { 97 throw new RuntimeException("执行更新失败!" +e.getMessage()); 98 } 99 return result; 100 } 101 102 /** 103 * 关闭资源 104 * 关闭ResultSet、PreparedStatement、Connection对象 105 */ 106 public void closeAll() 107 { 108 if(res != null) 109 { 110 try { 111 res.close(); 112 } catch (SQLException e) { 113 throw new RuntimeException("关闭ResultSet失败!"); 114 } 115 } 116 if(pstmt != null) 117 { 118 try { 119 pstmt.close(); 120 } catch (SQLException e) { 121 throw new RuntimeException("关闭PreparedStatement失败!"); 122 } 123 } 124 if(conn != null) 125 { 126 try { 127 conn.close(); 128 } catch (SQLException e) { 129 throw new RuntimeException("关闭Connection失败!"); 130 } 131 } 132 } 133 }
在访问网站首页的时候,需要加载数据库的数据怎么办呢?除了使用ajax,还可以在jsp里面加入如下代码:
<% try{ if(request.getAttribute("dynamicList") == null) request.getRequestDispatcher("initDynamicForIndexServlet").forward(request, response); }catch(Exception e){ } %>
到此,整个项目的笔记就结束了,由于刚开始写博客所以写的不是很好,请见谅!本人QQ:2632790902,欢迎指教不足之处!