事务
事务介绍:
就是一件事情,操作要不全部成功,要不全部失败。
mysql里的事务操作
通过一个命令(show variables like 'autocommit';)
得到 autocommit=on;意思是事务 都自动提交
每一条sql都是一个事务
手动事务的操作:(☆)
start transaction; 开始事务(不管是否自动提交)
commit;提交事务
rollback;事务回滚
通过以下命令,可以设置事务为手动提交
set autocommit=off;
注意:一旦设置手动提交后,不要使用start transaction再开启事务了,这时开启的是新事务
java里的事务操作
Connection接口里面的方法:☆
void setAutoCommit(boolean autoCommet):设置事务是否自动提交
void commit():提交事务
void rollback():事务回滚
Savepoint setSavePoint():设置一个还原点
void rollback(savepoint):还原到哪个还原点
public class AccountDemo { public static void main(String[] args) { //aaa->bbb转钱 Connection conn=null; PreparedStatement ps=null; ResultSet rs=null; Savepoint p=null; try { conn=JDBCUtils.getConnection(); //手动开启事务 conn.setAutoCommit(false); //先给a减钱 String sql="update account set money =money-? where name=?"; ps=conn.prepareStatement(sql); ps.setString(1, "500"); ps.setString(2, "aaa"); int i=ps.executeUpdate(); System.out.println(i); //设置还原点 p=conn.setSavepoint(); /*if(i==1){ throw new RuntimeException(); }*/ //给b加钱 sql="update account set money =money+? where name=?"; ps=conn.prepareStatement(sql); ps.setString(1, "500"); ps.setString(2, "bbb"); i=ps.executeUpdate(); System.out.println(i); } catch (Exception e) { e.printStackTrace(); //回滚事务 try { conn.rollback(p); } catch (SQLException e1) { e1.printStackTrace(); } }finally{ //提交事务 try { conn.commit(); } catch (SQLException e) { e.printStackTrace(); } JDBCUtils.closeResources(conn, ps, rs); } } }
事务的特性:(☆)
ACID
原子性:atomicity 要么全部成功,要么全部失败
一致性:consistency 事务执行之后,数据库状态与其它业务规则保持一致。例如:转账,总钱数不变
隔离性:isolation 在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。
持久性:durability 一旦事务提交或回滚后,必须持久化到数据库中
隔离性:
若不考虑隔离性,会出现的问题:
脏读:读取到别的事务没有提交的数据
不可重复读:在一个事务中,两次查询的结果不一致(内容 update)
虚读(幻读):在一个事务中,两次查询的结果不一致(条数,针对insert delete操作)
数据库的隔离级别:
read uncommitted:读未提交,最低的级别,上面的情况都可以发生
read committed:读已提交,可以避免脏读
repeatable read:可重复读,可以避免不可重复读
serializable:串行化,最高的等级,可以避免所有问题。
演示几种问题:
设置数据库的隔离级别:
set session transactioin isolation level 级别;
查看当前的隔离级别:
select @@tx_isolation;
mysql默认的隔离级别:repeatable read☆
oracle扩展的隔离级别:read committed☆
安全性:
serializable>repeatable read>read committed>read uncommitted
效率:反之
案例:
转账案例
解决service层需要传入connection参数:
Map<Thread,Connection> map
第一次开始事务的时候,先获取map.get(Thread.currentThread()) == ThreadLocal.get()
获取一个connection,开启事务,最后map.put(Thread.currentThread(),connection) == ThreadLocal.set()
在dao层可以直接使用,map.get(Thread.currentThread())就可以获取绑定的链接
执行的流程:
account.jsp:接收用户信息
accountServlet:接收参数,调用service,页面的跳转
accountService:
1.开启事务
conn.startTransaction() ThreadLocal作用:将当前的链接和当前的线程绑定起来
2.开始转账
3.一旦出现异常 获取当前线程绑定的链接调用rollBack()
4.最后,提交事务,获取当前线程绑定的链接调用commit(),然后关闭链接,最后和当前线程解绑
accountDao:
转入
转出
account.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1><font color="red">${msg }</font></h1>
<form method="post" action="${pageContext.request.contextPath }/account">
<table border="1">
<tr>
<td>汇款人:</td>
<td><input type="text" name="fromuser"></td>
</tr>
<tr>
<td>收款人:</td>
<td><input type="text" name="touser"></td>
</tr>
<tr>
<td>转账金额:</td>
<td><input type="text" name="money"></td>
</tr>
<tr>
<td colspan="2"><input type="submit"></td>
</tr>
</table>
</form>
</body>
</html>
JDBCUtil:
public class JDBCUtils_ {
static final String DRIVERCLASSNAME;
static final String URL;
static final String USER;
static final String PASSWORD;
static{
/**
* ResourceBundle:用于加载properties文件
* ResourceBundle bundle=ResourceBundle.getBundle(文件名称);
* 通过bundle的getString(key)就可以获取指定value
* String url=bundle.getString("url");
*/
ResourceBundle bundle=ResourceBundle.getBundle("jdbc"); //不需要后缀名
DRIVERCLASSNAME=bundle.getString("driverClassName");
URL=bundle.getString("url");
USER=bundle.getString("user");
PASSWORD=bundle.getString("password");
}
static{
try {
Class.forName(DRIVERCLASSNAME);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static ThreadLocal<Connection> tl=new ThreadLocal<Connection>();
/**
* 获取链接
* @return 链接
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
//第一次开启事务,先获取
Connection conn=tl.get();
if(conn==null){
//创建一个
conn= DriverManager.getConnection(URL,USER,PASSWORD);
//和当前线程绑定
tl.set(conn);
}
return conn;
}
/**
* 开启事务
* @throws SQLException
*/
public static void startTransaction() throws SQLException{
getConnection().setAutoCommit(false);
}
/**
* 事务回滚
* @throws SQLException
*/
public static void rooBack() throws SQLException{
getConnection().rollback();
}
/**
* 事务提交及关闭
* @throws SQLException
*/
public static void coommitAndClose() throws SQLException{
Connection conn=getConnection();
conn.commit();
conn.close();
tl.remove();//将链接移除
}
/**
* 做增删改的时候调用
* @param conn
* @param st
*/
public static void closeResources(Connection conn,Statement st){
if(st!=null){
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 做查询时调用
* @param conn
* @param st
* @param rs
*/
public static void closeResources(Connection conn,Statement st,ResultSet rs){
closeResources(st,rs);
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void closeResources(Statement st,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(st!=null){
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
AccountServlet:
public class AccountServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 接收参数
* 调用service
*/
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//0.设置编码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//1.接收参数
String fromUser=request.getParameter("fromuser");
String toUser=request.getParameter("touser");
String money=request.getParameter("money");
//2.调用service
AccountService accountService=new AccountService();
try {
accountService.account(fromUser, toUser, money);
} catch (MyException e) {
e.printStackTrace();
//添加消息,页面跳转
request.setAttribute("msg", e.getMessage());
request.getRequestDispatcher("/account.jsp").forward(request, response);
return;
}
//3.页面跳转
response.getWriter().print("转账成功");
}
}
AccountService:
public class AccountService_ {
public void account(String fromUser,String toUser,String money){
//开始事务
try {
JDBCUtils_.startTransaction();
AccountDao_ accountDao=new AccountDao_();
//出
accountDao.accountOut( fromUser, money);
//如
accountDao.accountIn(toUser,money);
} catch (SQLException e) {
e.printStackTrace();
//回滚
try {
JDBCUtils_.rooBack();
} catch (SQLException e1) {
e1.printStackTrace();
}
} catch (MyException e) {
e.printStackTrace();
}finally{
//提交
try {
JDBCUtils_.coommitAndClose();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
AccountDao:
public class AccountDao_ {
public void accountOut(String fromUser, String money) throws MyException {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=JDBCUtils_.getConnection();
String sql="upate account set money=money-? where name=?";
ps.setString(1, money);
ps.setString(2, fromUser);
int i=ps.executeUpdate();
if(i!=1){
throw new MyException("转出失败");
}
} catch (SQLException e) {
e.printStackTrace();
try {
JDBCUtils_.rooBack();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}finally{
try {
JDBCUtils_.coommitAndClose();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void accountIn(String toUser, String money) throws MyException, SQLException {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=JDBCUtils_.getConnection();
String sql="upate account set money=money+? where name=?";
ps.setString(1, money);
ps.setString(2, toUser);
int i=ps.executeUpdate();
if(i!=1){
throw new MyException("转入失败");
}
} catch (SQLException e) {
e.printStackTrace();
try {
JDBCUtils_.rooBack();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}finally{
JDBCUtils_.coommitAndClose();
}
}
}
Exception:
public class MyException extends Exception{
public MyException() {
super();
// TODO Auto-generated constructor stub
}
public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public MyException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public MyException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public MyException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
连接池:
连接池的优点:
1.提高了链接的复用性
2.节省了创建链接、销毁链接的时间,提高了性能
规范:
连接池都应该是DataSource的实现,datasource是一个规范,javax.sql包的一个接口
获取链接:
getConnection()
关闭的时候:
conn.close()
常见的连接池:
dbcp(理解):apache组织的
c3p0(☆):c3p0
tomcat内置的连接池(了解)
dbcp的使用:
1.导入jar包 commons-dbcp-1.4.jar(核心) 和commons-pool-1.5.6.jar(依赖)
2.编写代码(两种方式)
第一种方式:源码中配置
第二种方式:配置文件
dbcp.properties driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/day14 username=root password=moujinling321 DBCPDemo public class DBCPDemo { public static void main(String args[]) throws Exception { //method1(); method2(); } private static void method2() throws Exception { /* * 方式二:配置文件 */ BasicDataSourceFactory factory=new BasicDataSourceFactory(); Properties p=new Properties(); p.load(new FileInputStream("src/dbcp.properties")); DataSource ds=factory.createDataSource(p); Connection conn=ds.getConnection(); String sql="select * from account limit 1"; PreparedStatement ps=null; ResultSet rs=null; ps=conn.prepareStatement(sql); rs=ps.executeQuery(); while(rs.next()){ System.out.println(rs.getString(1)+":"+rs.getString(2)); } rs.close(); ps.close(); conn.close(); } private static void method1() throws SQLException{ /* * 方式一:源码中配置 */ BasicDataSource ds=new BasicDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/day14"); ds.setUsername("root"); ds.setPassword("moujinling321"); Connection conn=ds.getConnection(); String sql="select * from account"; PreparedStatement ps=null; ResultSet rs=null; ps=conn.prepareStatement(sql); rs=ps.executeQuery(); while(rs.next()){ System.out.println(rs.getString(1)+":"+rs.getString(2)); } rs.close(); ps.close(); conn.close(); } }
c3p0的使用:
项目中使用
自动回收空闲的链接
使用步骤:
1.导入jar包 c3p0-0.9.1.2.jar
2.编码
方式1:编码中编写配置(理解)
方式2:配置文件
c3p0.properties c3p0.jdbcUrl=jdbc:mysql://localhost:3306/day14 c3p0.driverClass=com.mysql.jdbc.Driver c3p0.user=root c3p0.password=moujinling321 C3P0Demo public class C3P0Demo { public static void main(String[] args) throws PropertyVetoException, SQLException { //method1(); method2(); } //第二种方式:配置文件(自动查找) private static void method2() throws SQLException { ComboPooledDataSource ds= new ComboPooledDataSource(); Connection conn=ds.getConnection(); String sql="select * from account limit 1"; PreparedStatement ps=null; ResultSet rs=null; ps=conn.prepareStatement(sql); rs=ps.executeQuery(); while(rs.next()){ System.out.println(rs.getString(1)+":"+rs.getString(2)); } rs.close(); ps.close(); conn.close(); } //第一种方式:源码中配置 private static void method1() throws PropertyVetoException, SQLException { ComboPooledDataSource ds= new ComboPooledDataSource(); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql://localhost:3306/day14"); ds.setUser("root"); ds.setPassword("moujinling321"); Connection conn=ds.getConnection(); String sql="select * from account"; PreparedStatement ps=null; ResultSet rs=null; ps=conn.prepareStatement(sql); rs=ps.executeQuery(); while(rs.next()){ System.out.println(rs.getString(1)+":"+rs.getString(2)); } rs.close(); ps.close(); conn.close(); } }
登录/注册案例:
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>bookStore 商城-商品信息展示</title>
</head>
<body>
<%@include file="/public/head.jsp" %>
<%@include file="/public/menu.jsp" %>
<!-- search 一行一列的表格 -->
<div id="">
<table width="100%" bgcolor="#B6B684">
<tr align="right">
<td>
search
<input type="text"/>
<input type="button" value="搜索"/>
</td>
</tr>
</table>
</div>
<!-- content -->
<div id="content">
<table align="center" bgcolor="#FFFFF4" width="60%">
<tr>
<td>
<h4>首页>>个人用户登录</h4>
<p ><font color="red">${login_msg }</font></p>
<!--下面整块是个table-->
<table align="center">
<tr>
<td>
<!-- 左边是table-->
<table width="300px" bgcolor="lightgray" margin-left="10px" height="350px">
<tr>
<td>
<!--上部分是提交表单-->
<form action="${pageContext.request.contextPath }/login" method="post">
<table>
<tr>
<td colspan="2" align="center"><h3>个人用户登录</h3></td>
</tr>
<tr>
<td>用户名:</td>
<td>
<input type="text" name="username" value="${cookie.savename.value }"/>
</td>
</tr>
<tr>
<td>密 码:</td>
<td>
<input type="password" name="password"/>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="checkbox" name="savename" value="ok"/>记住用户名
<input type="checkbox" name="checkbox" value="checkbox"/>自动登录
</td>
</tr>
<tr>
<td colspan="2" align="center" >
<input type="submit" value="登录" width="50px"/>
</td>
</tr>
</table>
</form>
</td>
</tr>
<tr>
<td colspan="2">
<hr/>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<a href="#" style="text-decoration:none;"><h4>注册新会员</h4></a>
</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td height="50px">
<h4>您还没注册?</h4>
</td>
</tr>
<tr>
<td>注册新会员,享受更优惠价格!</td>
</tr>
<tr>
<td height="50px">
千种图书,供你挑选!注册即享受丰富折扣和优惠,便宜有好货!超过千万本图书任您选。
</td>
</tr>
<tr>
<td height="50px">
超人气社区!精彩活动每一天!买卖更安心!支付宝交易超安全。
</td>
</tr>
<tr>
<td align="right" height="50px">
<a href="#" style="text-decoration:none;"><h4>注册新会员</h4></a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<!-- foot 两行两列的表格-->
<div id="foot">
<table width="100%" bgcolor="#EFEEDC">
<tr>
<td rowspan="2" align="center">
<img src="images/case1/logo.png" alt="图书商城"/>
</td>
<td>CONTACT US</td>
</tr>
<tr>
<td>
copyright 2008©rightBookStore All Rights RESERVED
</td>
</tr>
</table>
</div>
<table width="100%">
<tr>
<td></td>
</tr>
</table>
</body>
</html>
regist.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>bookStore 商城-商品信息展示</title>
</head>
<body>
<!-- head 一行两列的表格-->
<%@include file="/public/head.jsp" %>
<!-- menu 一行一列的表格-->
<%@include file="/public/menu.jsp" %>
<!-- search 一行一列的表格 -->
<div id="search">
<table width="100%" bgcolor="#B6B684">
<tr align="right">
<td>
search
<input type="text"/>
<input type="button" value="搜索"/>
</td>
</tr>
</table>
</div>
<!-- content -->
<div id="content">
<form action="${pageContext.request.contextPath}/regist" method="post">
<table align="center" bgcolor="#FFFFF4" width="60%">
<tr>
<td>
<h1>新会员注册<font color="red">${regist_msg }</font></h1>
<table align="center">
<tr>
<td align="right" >会员邮箱:</td>
<td>
<input type="text" name="email"/>
</td>
<td align="left">
<font color="#ff0000">${map.email_msg }</font>
</td>
</tr>
<tr>
<td align="right">会员名:</td>
<td>
<input type="text" name="username" class="txtinput"/>
</td>
<td align="left">
<font color="#ff0000"></font>
</td>
</tr>
<tr>
<td align="right">密码:</td>
<td>
<input type="password" name="password" class="txtinput"/>
</td>
<td align="left">
<font color="#ff0000"></font>
</td>
</tr>
<tr>
<td align="right">重复密码:</td>
<td>
<input type="text" name="repassword"/>
</td>
</tr>
<tr>
<td align="right">性别:</td>
<td>
<input type="radio" name="sex" value="男"/>男
<input type="radio" name="sex" value="女"/>女
</td>
<td></td>
</tr>
<tr>
<td align="right">联系电话:</td>
<td>
<input type="text" name="telephone"/>
</td>
</tr>
<tr>
<td align="right">个人介绍:</td>
<td>
<input type="textarea" name="introduce"/>
</td>
</tr>
</table>
<h1>注册校验</h1>
<table align="center" width="80%" cellspacing="2">
<tr>
<td align="right">输入校验码:</td>
<td>
<input type="text" name="checkcode" class="txtinput"/>
</td>
<td>${requestScope["code.msg"] }</td> <!-- code.msg,单独不可以写进去,.表示get。用域对象表示出来 -->
</tr>
<tr>
<td> </td>
<td colspan="2">
<img src="${pageContext.request.contextPath }/code" id="imgId" alt="验证码" class="txtinput" style="height:30px"/>
<a href="javascript:void(0)" οnclick="changeCode()">看不清?换一张</a>
<!-- 表示这个链接不做跳转动作,执行onClick事件。 -->
</td>
</tr>
<tr>
<td align="center" colspan="3">
<input value="同意并提交" style="background:orange;height:30px;width:100px;text-align:center;" type="submit"/>
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</div>
<!-- foot 两行两列的表格-->
<div id="foot">
<table width="100%" bgcolor="#EFEEDC">
<tr>
<td rowspan="2" align="center">
<img src="images/case1/logo.png" alt="图书商城"/>
</td>
<td>CONTACT US</td>
</tr>
<tr>
<td>
copyright 2008©rightBookStore All Rights RESERVED
</td>
</tr>
</table>
</div>
<table width="100%">
<tr>
<td></td>
</tr>
</table>
</body>
<script type="text/javascript">
function changeCode(){
var imgObj=document.getElementById("imgId");
imgObj.src="${pageContext.request.contextPath}/code?i="+Math.random();
}
</script>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:if test="${empty user }">
请先<a href="${pageContext.request.contextPath }/login.jsp">登录</a>
</c:if>
<c:if test="${!empty user }">
${user.username }:欢迎回来!
</c:if>
</body>
</html>
LoginServlet
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 获取参数
* 调用userservice的login
* 页面跳转,提示信息
*/
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8");
//1.获取参数
String username=request.getParameter("username");
String password=request.getParameter("password");
//2.调用service
UserService userService=new UserService();
User user=null;
try {
user = userService.login(username,password);
if(user==null){
request.setAttribute("login_msg", "用户名和密码不匹配");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}else{
//登录成功
//判断是否记住用户名
String savename=request.getParameter("savename");
if(Constant.IS_SAVA_NAME.equals(savename)){
Cookie c=new Cookie("savename",username);
c.setMaxAge(3600);
response.addCookie(c);
}
}
} catch (UserRegistException e) {
e.printStackTrace();
//将错误信息添加到request
request.setAttribute("login_msg", e.getMessage());
request.getRequestDispatcher("/login.jsp").forward(request, response);
} catch (SQLException e) {
e.printStackTrace();
}
//3.页面跳转
request.getSession().setAttribute("user", user);
response.sendRedirect(request.getContextPath()+"/success.jsp");
}
}
UserService
public class UserService {
UserDaoImpl userDao=new UserDaoImpl();
/**
* 用户注册
* @param user 用户信息
* @throws UserRegistException
*/
public void regist(User user) throws UserRegistException {
try {
//开启事务
DataSourceUtils.startTransaction();
/*
* 首先判断数据库中有没有同名的
* 若有,返回true,不能注册,提示信息
* 若无,可以添加
*/
boolean flag=userDao.findUserByUsername(user);
if(!flag){//flag=false
//调用userdao的adduser方法
userDao.addUser(user);
}else{
throw new UserRegistException("该用户名已被占用");
}
} catch (SQLException e) {
e.printStackTrace();
try {
DataSourceUtils.rollBack();
} catch (SQLException e1) {
e1.printStackTrace();
}
throw new UserRegistException("网络异常!!");
}finally{
//提交事务
try {
DataSourceUtils.commitAndClose();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 用户登录
* @param username 用户名
* @param password 密码
* @return 返回用户
* @throws UserRegistException
* @throws SQLException
*/
public User login(String username, String password) throws UserRegistException, SQLException {
User user=null;
try {
DataSourceUtils.startTransaction();
user=userDao.getUserByUsernameAndPwd(username,password);
} catch (SQLException e) {
e.printStackTrace();
try {
DataSourceUtils.rollBack();
} catch (SQLException e1) {
e1.printStackTrace();
}
throw e;
}finally{
try {
DataSourceUtils.commitAndClose();
} catch (SQLException e) {
e.printStackTrace();
}
}
return user;
}
}
RegistServlet
public class RegistServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8");
/*
* 作用:首先判断验证码
* 若验证码不一致,返回错误信息(regist.jsp) 提示信息(验证码错误) request
* 验证码一致,封装参数,调用userservice
*/
//1.获取验证码
//1.1获取页面上验证码
String code=request.getParameter("checkcode");
//1.2获取session域中的验证码
String sessionCode=(String) request.getSession().getAttribute("session_code");
request.getSession().removeAttribute("session_code"); //验证码一次性
//2.判断验证码
//输入验证码不正确
if(!sessionCode.equalsIgnoreCase(code)){
//加入错误信息,页面跳转
request.setAttribute("code.msg", "验证码输入错误");
request.getRequestDispatcher("/regist.jsp").forward(request,response);
return;
}
//3.获取参数
User user=new User();
try {
BeanUtils.populate(user, request.getParameterMap());
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
//3.1数据校验
//验证邮件
Map<String,String> map=user.validate();
if(map!=null && map.size()>0){
//有错误信息
request.setAttribute("map", map);
request.getRequestDispatcher("/regist.jsp").forward(request, response);
return;
}
//4.调用userservice
UserService userService=new UserService();
//可能抛自定义异常
try {
userService.regist(user);
} catch (UserRegistException e) {
e.printStackTrace();
//有异常,添加错误信息,跳转到注册页面
request.setAttribute("regist_msg", e.getMessage());
request.getRequestDispatcher("/regist.jsp").forward(request, response);
}
//5.页面跳转,提示信息
//5.1将user放到session中
request.getSession().setAttribute("user", user);
//5.2提示信息 页面跳转
response.setHeader("refresh", "3;url="+request.getContextPath()+"/success.jsp");
response.getWriter().print("注册成功,3秒之后跳转到首页!");
return;
}
}
UserService
public class UserService {
UserDaoImpl userDao=new UserDaoImpl();
/**
* 用户注册
* @param user 用户信息
* @throws UserRegistException
*/
public void regist(User user) throws UserRegistException {
try {
//开启事务
DataSourceUtils.startTransaction();
/*
* 首先判断数据库中有没有同名的
* 若有,返回true,不能注册,提示信息
* 若无,可以添加
*/
boolean flag=userDao.findUserByUsername(user);
if(!flag){//flag=false
//调用userdao的adduser方法
userDao.addUser(user);
}else{
throw new UserRegistException("该用户名已被占用");
}
} catch (SQLException e) {
e.printStackTrace();
try {
DataSourceUtils.rollBack();
} catch (SQLException e1) {
e1.printStackTrace();
}
throw new UserRegistException("网络异常!!");
}finally{
//提交事务
try {
DataSourceUtils.commitAndClose();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 用户登录
* @param username 用户名
* @param password 密码
* @return 返回用户
* @throws UserRegistException
* @throws SQLException
*/
public User login(String username, String password) throws UserRegistException, SQLException {
User user=null;
try {
DataSourceUtils.startTransaction();
user=userDao.getUserByUsernameAndPwd(username,password);
} catch (SQLException e) {
e.printStackTrace();
try {
DataSourceUtils.rollBack();
} catch (SQLException e1) {
e1.printStackTrace();
}
throw e;
}finally{
try {
DataSourceUtils.commitAndClose();
} catch (SQLException e) {
e.printStackTrace();
}
}
return user;
}
}
UserDao
public class UserDaoImpl implements UserDao{
/**
* 添加用户
* @param user 用户信息
* @throws UserRegistException
* @throws SQLException
*/
public void addUser(User user) throws UserRegistException {
//使用JDBC操作数据库
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=DataSourceUtils.getConnection();
//编写sql
//username,password,sex,telephone,email,introduce
String sql="insert into user values(null,?,?,?,?,?,?)";
//创建语句执行者
ps=conn.prepareStatement(sql);
//设置参数
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getSex());
ps.setString(4, user.getTelephone());
ps.setString(5, user.getEmail());
ps.setString(6, user.getIntroduce());
//执行sql
int i=ps.executeUpdate();
if(i==0){
throw new UserRegistException("注册失败");
}
//处理结果
System.out.println(i);
} catch (SQLException e) {
// 数据库异常
e.printStackTrace();
throw new UserRegistException("网络异常,请稍后再试");
}finally{
//释放资源
DataSourceUtils.closeResources(ps, rs);
}
}
/**
* 通过用户名查找用户
* 若存在返回true
* 若不存在返回false
*/
public boolean findUserByUsername(User user) {
//使用JDBC操作数据库
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=DataSourceUtils.getConnection();
String sql="select * from user where username=? limit 1";
ps=conn.prepareStatement(sql);
ps.setString(1, user.getUsername());
rs=ps.executeQuery();
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
}finally{
DataSourceUtils.closeResources(ps, rs);
}
return false;
}
/**
* 通过用户名和密码获取user 登录
* @param username
* @param password
* @return
* @throws UserRegistException
*/
public User getUserByUsernameAndPwd(String username, String password) throws UserRegistException {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=DataSourceUtils.getConnection();
String sql="select * from user where username=? and password =? limit 1";
ps=conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
rs=ps.executeQuery();
if(rs.next()){
return new User(rs.getInt("id"),rs.getString("email"),rs.getString("username"),rs.getString("password"),rs.getString("sex"),rs.getString("introduce"), rs.getString("telephone"));
}
} catch (SQLException e) {
e.printStackTrace();
throw new UserRegistException("网络异常,请稍后再试");
}finally{
DataSourceUtils.closeResources(ps, rs);
}
return null;
}
}
User
public class User {
private Integer id;
private String email;
private String username;
private String password;
private String sex;
private String introduce;
private String telephone;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getIntroduce() {
return introduce;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public User(Integer id, String email, String username, String password, String sex, String introduce,
String telephone) {
super();
this.id = id;
this.email = email;
this.username = username;
this.password = password;
this.sex = sex;
this.introduce = introduce;
this.telephone = telephone;
}
public User() {
super();
}
@Override
public String toString() {
return "User [id=" + id + ", email=" + email + ", username=" + username + ", password=" + password + ", sex="
+ sex + ", introduce=" + introduce + ", telephone=" + telephone + "]";
}
public Map<String, String> validate() {
//创建Map
Map<String,String> map=new HashMap();
//验证邮箱
/*
* 首先验证邮箱是否为空
* 若为空,提示邮箱不能为空
* 若不为空,继续验证邮箱是否符合格式
* 若不符合,提示邮箱格式不争取
*
*/
if(isNull(email)){
map.put("email_msg", "邮箱不能为空");
}else if(!checkEmail(email)){
map.put("email_msg", "邮箱格式不合法");
}
return map;
}
/**
* 判断邮箱格式
* 若符合格式,返回true
* 若不符合格式,返回false
* @param value
* @return bolean
*/
private boolean checkEmail(String value) {
if(value==null){
return false;
}
//qwer@qq.com.cn
String reg="^\\w+@\\w+(\\.\\w+)+$";
return value.matches(reg);
}
/**
* 验证是否为空
* 若为空返回Null
* 若不为空返回false
* @param value
* @return boolean
*/
private boolean isNull(String value) {
//^\s*&
if(value==null){
return true;
}
return value.matches("^\\s*$");
}
}