第18天(就业班) 预编译sql处理(防止sql注入)、存储过程、批处理、插入数据获取增长值、事务、大文本类型处理

2/10/2017来源:ASP.NET技巧人气:1464

一、预编译sql处理(防止sql注入)

--创建数据库

CREATEDATABASE jdbc_demo DEFAULT CHARACTER SET utf8;i

--创建表

USEjdbc_demo;

CREATETABLE admin(

    id INT PRIMARY KEY AUTO_INCREMENT,

    userName VARCHAR(20),

    pwd VARCHAR(20)

)

|--Statement      执行SQL命令

   |-- CallableStatement,     执行存储过程

|--PreparedStatement    预编译SQL语句执行

使用预编译SQL语句的命令对象,好处:

1. 避免了频繁sql拼接 (可以使用占位符)

2. 可以防止sql注入

登陆模块,

   输入用户名,密码!

注意,

要避免用户输入的恶意密码!

package com.xp.jdbc.pstmt;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

public class PstmtLogin {
	// 连接参数
	// private String url = "jdbc:MySQL://localhost:3306/jdbc_demo";
	private String url = "jdbc:mysql:///jdbc_demo";
	private String user = "root";
	private String passWord = "root";

	private Connection con;
	private Statement stmt;
	private PreparedStatement pstmt;
	private ResultSet rs;

	// 1. 没有使用防止sql注入的案例
	@Test
	public void testLogin() {

		// 1.0 模拟登陆的用户名,密码
		String userName = "tom";
		// String pwd = "8881";
		String pwd = " ' or 1=1 -- ";

		// SQL语句
		String sql = "select * from admin where userName='" + userName
				+ "'  and pwd='" + pwd + "' ";
		System.out.println(sql);
		try {
			// 1.1 加载驱动,创建连接
			Class.forName("com.mysql.jdbc.Driver");
			con = DriverManager.getConnection(url, user, password);
			// 1.2 创建stmt对象
			stmt = con.createStatement();
			// 1.3 执行查询
			rs = stmt.executeQuery(sql);
			// 业务判断
			if (rs.next()) {
				System.out.println("登陆成功, 编号:" + rs.getInt("id"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 1.4 关闭
			try {
				rs.close();
				stmt.close();
				con.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	// 2. 使用PreparedStatement, 防止sql注入
	@Test
	public void testLogin2() {

		// 1.0 模拟登陆的用户名,密码
		String userName = "tom";
		// String pwd = "8881";
		String pwd = " ' or 1=1 -- ";

		// SQL语句
		String sql = "select * from admin where userName=?  and pwd=? ";
		try {
			// 1.1 加载驱动,创建连接
			Class.forName("com.mysql.jdbc.Driver");
			con = DriverManager.getConnection(url, user, password);
			// 1.2 创建pstmt对象
			pstmt = con.prepareStatement(sql); // 对sql语句预编译
			// 设置占位符值
			pstmt.setString(1, userName);
			pstmt.setString(2, pwd);

			// 1.3 执行
			rs = pstmt.executeQuery();
			if (rs.next()) {
				System.out.println("登陆成功," + rs.getInt("id"));
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 1.4 关闭
			try {
				rs.close();
				pstmt.close();
				con.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}二、 存储过程调用

-- 存储过程
DELIMITER $$
CREATE PROCEDURE proc_login()
BEGIN
	SELECT * FROM admin;
END $$
-- 调用
CALL proc_login;
package com.xp.jdbc.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JdbcUtil {
	// 连接参数
		// private String url = "jdbc:mysql://localhost:3306/jdbc_demo";
		private static String url = "jdbc:mysql:///jdbc_demo";
		private static String user = "root";
		private static String password = "xiongpan";

		/**
		 * 返回连接对象
		 */
		public static Connection getConnection() {
			try {
				Class.forName("com.mysql.jdbc.Driver");
				return DriverManager.getConnection(url, user, password);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		/**
		 * 关闭
		 */
		public static void closeAll(Connection con, Statement stmt, ResultSet rs) {
			try {
				if (rs != null) {
					rs.close();  // 快速异常捕获 Alt + shift + z 
					rs = null;   // 建议垃圾回收期回收资源
				}
				if (stmt != null) {
					stmt.close();
					stmt = null;
				}
				if (con != null && !con.isClosed()) {
					con.close();
					con = null;
				}
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
	}

package com.xp.jdbc.pstmt;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

import com.xp.jdbc.utils.JdbcUtil;

public class PstmtLogin_call {

	// 全局参数
	private Connection con;
	private Statement stmt;
	private PreparedStatement pstmt;
	private CallableStatement cstmt; // 存储过程
	private ResultSet rs;

	// 程序中调用存储过程
	@Test
	public void testCall() throws Exception {

		try {
			// 1 . 创建连接
			con = JdbcUtil.getConnection();
			// 2. 创建执行存储过程的stmt对象
			CallableStatement cstmt = con.prepareCall("CALL proc_login");
			// 3. 执行(存储过程)
			rs = cstmt.executeQuery();

			// 遍历结果,测试
			if (rs.next()) {
				String name = rs.getString("userName");
				String pwd = rs.getString("pwd");
				// 测试
				System.out.println(name + pwd);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

一、 批处理

很多时候,需要批量执行sql语句!

需求:批量保存信息! 

设计:

   AdminDao

      Public  void save(List<Admin list){    // 目前用这种方式

        // 循环

        // 保存 (批量保存)

      }

      Public  void save(Admin  admin ){

        // 循环

        // 保存

      }

技术:

   |-- Statement

   批处理相关方法

      voidaddBatch(String sql)     添加批处理

      voidclearBatch()            清空批处理

int[] executeBatch()         执行批处理

实现:

   Admin.java         实体类封装数据

   AdminDao.java      封装所有的与数据库的操作

   App.java           测试

package com.xp.batch;

public class Admin {
	private String userName;
	private String pwd;
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
	
}

package com.xp.batch;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;

import com.xp.jdbc.utils.JdbcUtil;

public class AdminDao {
	// 全局参数
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet rs;

	// 批量保存管理员
	public void save(List<Admin> list) {
		// SQL
		String sql = "INSERT INTO admin(userName,pwd) values(?,?)";

		try {

			// 获取连接
			con = JdbcUtil.getConnection();
			// 创建stmt
			pstmt = con.prepareStatement(sql); // 【预编译SQL语句】

			for (int i = 0; i < list.size(); i++) {
				Admin admin = list.get(i);
				// 设置参数
				pstmt.setString(1, admin.getUserName());
				pstmt.setString(2, admin.getPwd());

				// 添加批处理
				pstmt.addBatch(); // 【不需要传入SQL】

				// 测试:每5条执行一次批处理
				if (i % 5 == 0) {
					// 批量执行
					pstmt.executeBatch();
					// 清空批处理
					pstmt.clearBatch();
				}

			}
			// 批量执行
			pstmt.executeBatch();
			// 清空批处理
			pstmt.clearBatch();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, rs);
		}
	}
}
package com.xp.batch;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class App {

	// 测试批处理操作
	@Test
	public void testBatch() throws Exception {

		// 模拟数据
		List<Admin> list = new ArrayList<Admin>();
		for (int i = 1; i < 21; i++) {
			Admin admin = new Admin();
			admin.setUserName("Jack" + i);
			admin.setPwd("888" + i);
			list.add(admin);
		}

		// 保存
		AdminDao dao = new AdminDao();
		dao.save(list);
	}
}四、 插入数据,获取自增长值


 需求:  
李俊杰     18
张相       19


如何设计数据库?
编号    员工姓名    年龄    部门
01       李俊杰      18     开发部
02       张三        19     开发部’
思考:
如何减少数据冗余?
 设置外键约束


所以,
编号    员工姓名    年龄    部门
01       李俊杰      18     1
02       张三        19     1


部门编号     部门名称    
1             开发部            


部门与员工,
一对多的关系


 设计数据库:
员工表 (外键表) 【员工表有一个外键字段,引用了部门表的主键】
部门表(主键表)


 编码总体思路:
保存员工及其对应的部门!
步骤:
1. 先保存部门
2. 再得到部门主键,再保存员工


开发具体步骤:
1. 设计javabean
2. 设计dao
3. 测试

-- 部门
CREATE TABLE dept(
   deptId INT PRIMARY KEY AUTO_INCREMENT,
   deptName VARCHAR(20)
);
-- 员工
CREATE TABLE employee(
   empId INT PRIMARY KEY AUTO_INCREMENT,
   empName VARCHAR(20),
   dept_id  INT   --  外键字段   
);

-- 给员工表添加外键约束
ALTER TABLE employee ADD CONSTRAINT FK_employee_dept_deptId
	FOREIGN KEY(dept_id) REFERENCES dept(deptId) ;
package com.xp.auto;

public class Employee {

	private int empId;
	private String empName;
	// 关联的部门
	private Dept dept;

	public Dept getDept() {
		return dept;
	}

	public void setDept(Dept dept) {
		this.dept = dept;
	}

	public int getEmpId() {
		return empId;
	}

	public void setEmpId(int empId) {
		this.empId = empId;
	}

	public String getEmpName() {
		return empName;
	}

	public void setEmpName(String empName) {
		this.empName = empName;
	}

}
package com.xp.auto;

public class Dept {

	private int id;
	private String deptName;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getDeptName() {
		return deptName;
	}
	public void setDeptName(String deptName) {
		this.deptName = deptName;
	}
	
	
}
package com.xp.auto;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import com.xp.jdbc.utils.JdbcUtil;


public class EmpDao {
	
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet rs;

	// 保存员工,同时保存关联的部门
	public void save(Employee emp){
		
		// 保存部门
		String sql_dept = "insert into dept(deptName) values(?)";
		// 保存员工
		String sql_emp = "INSERT INTO employee (empName,dept_id) VALUES (?,?)";
		// 部门id
		int deptId = 0;
		
		try {
			// 连接
			con = JdbcUtil.getConnection();
			
			/*****保存部门,获取自增长*******/
			// 【一、需要指定返回自增长标记】
			pstmt = con.prepareStatement(sql_dept,Statement.RETURN_GENERATED_KEYS);
			// 设置参数
			pstmt.setString(1, emp.getDept().getDeptName());
			// 执行
			pstmt.executeUpdate();
			
			// 【二、获取上面保存的部门子增长的主键】
			rs =  pstmt.getGeneratedKeys();
			// 得到返回的自增长字段
			if (rs.next()) {
				deptId = rs.getInt(1);
			}
			
			/*****保存员工*********/
			pstmt = con.prepareStatement(sql_emp);
			// 设置参数
			pstmt.setString(1, emp.getEmpName());
			pstmt.setInt(2, deptId);
			pstmt.executeUpdate();
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, rs);
		}
	}
}
package com.xp.auto;
import org.junit.Test;
public class App {
	// 保存员工
	@Test
	public void testSave() throws Exception {
		// 模拟数据
		Dept d = new Dept();
		d.setDeptName("应用开发部");
		Employee emp = new Employee();
		emp.setEmpName("李俊杰");
		emp.setDept(d);   // 关联
		// 调用dao保存
		EmpDao empDao = new EmpDao();
		empDao.save(emp);
	}
}

五、事务

事务使指一组最小逻辑操作单元,里面有多个操作组成。组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。

事务ACID特性

 原子性(Atomicity)

原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

 一致性(Consistency)

事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

 隔离性(Isolation)

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

 持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

事务的特性:

原子性,是一个最小逻辑操作单元 !

一致性,事务过程中,数据处于一致状态。

持久性, 事务一旦提交成功,对数据的更改会反映到数据库中。

隔离性, 事务与事务之间是隔离的。

案例:

需求: 张三给李四转账

设计: 账户表

技术:

|--Connection

voidsetAutoCommit(boolean autoCommit) ;  设置事务是否自动提交如果设置为false,表示手动提交事务。

voidcommit() ();                   手动提交事务

voidrollback() ;                   回滚(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态。)

SavepointsetSavepoint(String name)

-- 账户表
CREATE TABLE account(
   id INT PRIMARY KEY AUTO_INCREMENT,
   accountName VARCHAR(20),
   money DOUBLE
);

-- 转账
UPDATE account SET money=money-1000 WHERE accountName='张三';
UPDATE account SET money=money+1000 WHERE accountName='李四';
package com.xp.account;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

import com.xp.jdbc.utils.JdbcUtil;

public class AccountDao {

	// 全局参数
	private Connection con;
	private PreparedStatement pstmt;

	// 1. 转账,没有使用事务
	public void trans1() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JdbcUtil.getConnection(); // 默认开启的隐士事务
			con.setAutoCommit(true);

			/*** 第一次执行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次执行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}

	}

	// 2. 转账,使用事务
	public void trans2() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls = "UPDATE1 account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JdbcUtil.getConnection(); // 默认开启的隐士事务
			// 一、设置事务为手动提交
			con.setAutoCommit(false);

			/*** 第一次执行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次执行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			try {
				// 二、 出现异常,需要回滚事务
				con.rollback();
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 三、所有的操作执行成功, 提交事务
				con.commit();
				JdbcUtil.closeAll(con, pstmt, null);
			} catch (SQLException e) {
			}
		}

	}

	// 3. 转账,使用事务, 回滚到指定的代码段
	public void trans() {
		// 定义个标记
		Savepoint sp = null;

		// 第一次转账
		String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

		// 第二次转账
		String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='张三';";
		String sql_ls2 = "UPDATE1 account SET money=money+500 WHERE accountName='李四';";

		try {
			con = JdbcUtil.getConnection(); // 默认开启的隐士事务
			con.setAutoCommit(false); // 设置事务手动提交

			/*** 第一次转账 ***/
			pstmt = con.prepareStatement(sql_zs1);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls1);
			pstmt.executeUpdate();

			// 回滚到这个位置?
			sp = con.setSavepoint();

			/*** 第二次转账 ***/
			pstmt = con.prepareStatement(sql_zs2);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls2);
			pstmt.executeUpdate();

		} catch (Exception e) {
			try {
				// 回滚 (回滚到指定的代码段)
				con.rollback(sp);
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 提交
				con.commit();
			} catch (SQLException e) {
			}
			JdbcUtil.closeAll(con, pstmt, null);
		}

	}
}
package com.xp.account;

import org.junit.Test;

public class App {
	@Test
	public void testname() throws Exception {
		// 转账
		AccountDao accountDao = new AccountDao();
		accountDao.trans();
	}
}六、Jdbc中大文本类型的处理
Oracle中大文本数据类型,
Clob    长文本类型   (MySQL中不支持,使用的是text)
Blob    二进制类型

MySQL数据库,
Text    长文本类型
Blob    二进制类型
需求: jdbc中操作长文本数据。
设计: 测试表
编码:
保存大文本数据类型
读取大文本数据类型


保存二进制数据
读取二进制数据
-- 测试大数据类型
CREATE TABLE test(
     id INT PRIMARY KEY AUTO_INCREMENT,
     content LONGTEXT,
     img LONGBLOB
);
package com.xp.longtext;

import java.io.File;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

import com.xp.jdbc.utils.JdbcUtil;

public class App_text {
	// 全局参数
	private Connection con;
	private Statement stmt;
	private PreparedStatement pstmt;
	private ResultSet rs;

	@Test
	// 1. 保存大文本数据类型 ( 写longtext)
	public void testSaveText() {
		String sql = "insert into test(content) values(?)";
		try {
			// 连接
			con = JdbcUtil.getConnection();
			// pstmt 对象
			pstmt = con.prepareStatement(sql);
			// 设置参数
			// 先获取文件路径
			String path = App_text.class.getResource("tips.txt").getPath();
			FileReader reader = new FileReader(new File(path));
			pstmt.setCharacterStream(1, reader);

			// 执行sql
			pstmt.executeUpdate();

			// 关闭
			reader.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}
	}

	@Test
	// 2. 读取大文本数据类型 ( 读longtext)
	public void testGetAsText() {
		String sql = "select * from  test;";
		try {
			// 连接
			con = JdbcUtil.getConnection();
			// pstmt 对象
			pstmt = con.prepareStatement(sql);
			// 读取
			rs = pstmt.executeQuery();
			if (rs.next()) {
				// 获取长文本数据, 方式1:
				// Reader r = rs.getCharacterStream("content");

				// 获取长文本数据, 方式2:
				System.out.print(rs.getString("content"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}
	}
}
package com.xp.longtext;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

import com.xp.jdbc.utils.JdbcUtil;

public class App_blob {
	// 全局参数
	private Connection con;
	private Statement stmt;
	private PreparedStatement pstmt;
	private ResultSet rs;

	@Test
	// 1. 二进制数据类型 ( 写longblob)
	public void testSaveText() {
		String sql = "insert into test(img) values(?)";
		try {
			// 连接
			con = JdbcUtil.getConnection();
			// pstmt 对象
			pstmt = con.prepareStatement(sql);
			// 获取图片流
			InputStream in = App_text.class.getResourceAsStream("7.jpg");
			pstmt.setBinaryStream(1, in);

			// 执行保存图片
			pstmt.execute();

			// 关闭
			in.close();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}
	}

	@Test
	// 2. 读取大文本数据类型 ( 读longblob)
	public void testGetAsText() {
		String sql = "select img from  test where id=2;";
		try {
			// 连接
			con = JdbcUtil.getConnection();
			// pstmt 对象
			pstmt = con.prepareStatement(sql);
			// 读取
			rs = pstmt.executeQuery();
			if (rs.next()) {
				// 获取图片流
				InputStream in = rs.getBinaryStream("img");
				// 图片输出流
				FileOutputStream out = new FileOutputStream(new File(
						"c://1.jpg"));
				int len = -1;
				byte b[] = new byte[1024];
				while ((len = in.read(b)) != -1) {
					out.write(b, 0, len);
				}
				// 关闭
				out.close();
				in.close();
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}
	}
}

五、综合案例

需求分析:

   登陆、注册、注销;

   登陆成功,

      显示所有的员工

设计

数据库设计:

   Admin, 存放所有的登陆用户

   Employee, 存放所有的员工信息

系统设计

a. 系统结构

分层: 基于mvc模式的分层

b. 项目用到的公用组件、类 (了解)

CREATE TABLE admin(
    id INT PRIMARY KEY AUTO_INCREMENT,
    userName VARCHAR(20),
    pwd VARCHAR(20)
)
-- 员工
CREATE TABLE employee(
   empId INT PRIMARY KEY AUTO_INCREMENT,
   empName VARCHAR(20),
   dept_id  INT   --  外键字段   
);
package com.xp.entity;
public class Admin {
	private int id;
	private String userName;
	private String pwd;
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
}

package com.xp.dao;

import com.xp.entity.Admin;
public interface IAdminDao {
	/**
	 * 保存
	 * @param admin
	 */
	void save(Admin admin);
	/**
	 * 根据用户名密码查询
	 */
	Admin findByNameAndPwd(Admin admin);
	
	/**
	 * 检查用户名是否存在
	 * @param name   要检查的用户名
	 * @return  true表示用户名已经存在; 否则用户名不存在
	 */
	boolean userExists(String name);
}

package com.xp.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.xp.dao.IAdminDao;
import com.xp.entity.Admin;
import com.xp.utils.JdbcUtil;

/**
 * 2. 数据访问层接口的实现类
*/
public class AdminDao implements IAdminDao {
	
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet rs;
	

	@Override
	public Admin findByNameAndPwd(Admin admin) {
		String sql = "select * from admin where userName=? and pwd=?";
		Admin ad = null;
		try {
			con = JdbcUtil.getConnection();
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, admin.getUserName());
			pstmt.setString(2, admin.getPwd());
			// 执行
			rs = pstmt.executeQuery();
			// 遍历
			if (rs.next()) {
				ad = new Admin();
				ad.setId(rs.getInt("id"));
				ad.setUserName(rs.getString("userName"));
				ad.setPwd(rs.getString("pwd"));
			}
			return ad;
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}
	}

	@Override
	public void save(Admin admin) {
		String sql = "INSERT INTO admin(userName,pwd) VALUES(?,?);";
		try {
			con = JdbcUtil.getConnection();
			pstmt = con.prepareStatement(sql);
			// 设置参数
			pstmt.setString(1, admin.getUserName());
			pstmt.setString(2, admin.getPwd());
			// 执行更新
			pstmt.executeUpdate();		
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			JdbcUtil.closeAll(con, pstmt, null);
		}
		
	}

	@Override
	public boolean userExists(String name) {
		String sql = "select id from admin where userName=?";
		try {
			con = JdbcUtil.getConnection();
			pstmt = con.prepareStatement(sql);
			// 设置参数
			pstmt.setString(1, name);
			// 执行查询
			rs = pstmt.executeQuery();
			// 判断
			if (rs.next()) {
				int id = rs.getInt("id");
				if (id > 0) {
					// 用户名已经存在
					return true;
				}
			}
			return false;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			JdbcUtil.closeAll(con, pstmt, rs);
		}
	}

}

package com.xp.service;

import com.xp.entity.Admin;
import com.xp.exception.UserExistsException;

public interface IAdminService {

	/**
	 * 注册
	 */
	void register(Admin admin) throws UserExistsException;
	
	/**
	 * 登陆
	 */
	Admin login(Admin admin);
}
package com.xp.exception;
public class UserExistsException extends Exception {
	public UserExistsException() {
	}
	public UserExistsException(String message) {
		super(message);
	}
	public UserExistsException(Throwable cause) {
		super(cause);
	}
	public UserExistsException(String message, Throwable cause) {
		super(message, cause);
	}
}
package com.xp.service.impl;

import com.xp.dao.IAdminDao;
import com.xp.dao.impl.AdminDao;
import com.xp.entity.Admin;
import com.xp.exception.UserExistsException;
import com.xp.service.IAdminService;

/**
 * 3. 业务逻辑层实现
 * 
 * @author Jie.Yuan
 * 
 */
public class AdminService implements IAdminService {

	// 调用的dao
	private IAdminDao adminDao = new AdminDao();

	@Override
	public Admin login(Admin admin) {
		try {
			return adminDao.findByNameAndPwd(admin);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void register(Admin admin) throws UserExistsException {

		try {
			// 1. 先根据用户名查询用户是否存在
			boolean flag = adminDao.userExists(admin.getUserName());

			// 2. 如果用户存在,不允许注册
			if (flag) {
				// 不允许注册, 给调用者提示
				throw new UserExistsException("用户名已经存在,注册失败!");
			}
			// 3. 用户不存在,才可以注册
			adminDao.save(admin);

		} catch (UserExistsException e) {
			throw e;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

}
package com.xp.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.xp.entity.Admin;
import com.xp.exception.UserExistsException;
import com.xp.service.IAdminService;
import com.xp.service.impl.AdminService;

public class AdminServlet extends HttpServlet {
	// 调用的service
	private IAdminService adminService = new AdminService();

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		this.doGet(request, response);
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		// 获取操作类型
		String method = request.getParameter("method");
		if ("register".equals(method)) {
			register(request, response);
		}
	}

	/**
	 * 注册处理方法
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void register(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {

		// 1. 获取请求参数
		String userName = request.getParameter("userName");
		String pwd = request.getParameter("pwd");
		// 封装
		Admin admin = new Admin();
		admin.setUserName(userName);
		admin.setPwd(pwd);

		// 2. 调用Service处理注册的业务逻辑
		try {
			adminService.register(admin);

			// 注册成功,跳转到首页
			request.getRequestDispatcher("/index.jsp").forward(request,
					response);

		} catch (UserExistsException e) {
			// 用户名存在,注册失败(跳转到注册页面)
			request.setAttribute("message", "用户名已经存在");
			// 转发
			request.getRequestDispatcher("/register.jsp").forward(request,
					response);
		} catch (Exception e) {
			e.printStackTrace(); // 测试时候用
			// 其他错误, 跳转到错误页面
			response.sendRedirect(request.getContextPath() + "/error/error.jsp");
		}
	}

}
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    
    <title>注册</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
  </head>
  
  <body>
  	<form name="frm1" action="${pageContext.request.contextPath }/admin?method=register" method="post" >
  		<table>
  			<tr>
  				<td>用户名</td>
  				<td>
  					<input type="text" name="userName"/>
  					${requestScope.message } <!-- 如果用户名存在注册失败,给用户提示 -->
  				</td>
  			</tr>
  			<tr>
  				<td>密码</td>
  				<td><input type="password" name="pwd"/></td>
  			</tr>
  			<tr>
  				<td colspan="2">
  					<input type="submit" value="亲,点我注册!">
  				</td>
  			</tr>
  		</table>
  	
  	</form>
  </body>
</html>