1、JavaEE三层架构
客户端提交发送到后端的数据,会先在Web层获取并封装成Bean对象,然后调用Service层的方法处理业务,再调用Dao层的方法与数据库交互实现数据的持久化存储
分层的目的是为了解耦,所谓的解耦就是降低代码的耦合度,方便项目后期的维护与升级。
在BookS这个项目中,src文件夹用来存放源代码,web用来存放页面资源
在src这个文件夹中有一个总包“com.chang”存在,以及存放连接数据库所需的配置文件“jdbc.properties”。
在“com.chang”包中又存在6个包,其各自功能如下:
包名 | 作用 |
---|---|
dao | 服务于service层,实现业务数据的增删改查。 |
pojo | 存放业务所需要的对象,例如User对象 |
service | 存放针对某一对象所要做的操作,例如增删改查 |
test | 针对一些工具类、或者对象方法进行测试 |
utils | 工具类,用来存放一些工具,例如连接数据库 |
web | 视图层,通过servlet获取前端的数据 |
针对具体使用流程,可将上述表格中的内容稍作调整,(代码编写过程中可将第2到第5顺序调换 1->5->4->3->2->6),6可以穿插进行
包名 | 作用 |
---|---|
pojo | 存放User对象 |
web | 获取前端数据,例如名称、密码、验证码,然后可构建一个User对象,针对次对象,要进行业务处理,则需要用到service包中UserService类,“private UserService userService = new UserServiceImpl();”然后使用相关方法“userService.registUser(User);” |
service | 为了更好的解耦,首先编写UserService的接口,然后在impl包中编写UserServiceImpl实现类,在此类中因为要实现与数据库的交互,所以要在dao包中实现UserDao类,“private UserDao userDao = new UserDaoImpl();”,“userDao.saveUser(user);” |
dao | 在此包中除了要编写实现某类对象数据库操作的接口以及实现类,还要编写基础类,具体实现类要继承实现类并实现接口方法,例如UserDaoImpl extends BaseDao implements UserDao。在BaseDao类中要实现具体的增删改查,则要使用utils包中的JdbcUtils类,以及一些其他的官方包“private QueryRunner queryRunner=new QueryRunner();” |
utils | 在这个包中编写JdbcUtils方法读取src目录下的配置文件,实现与数据库的连接、关闭操作 |
test | 在这个包中对实现的方法进行测试,例如JdbcUtils、UserService、UserDao |
2、创建项目需要的数据库和表
在终端的MySQL中输入如下命令:
drop database if exists book;
create database book;
use book;
create table t_user(
`id` int primary key auto_increment,
`username` varchar(20) not null unique,
`password` varchar(32) not null,
`email` varchar(200)
);
insert into t_user(`username`,`password`,`email`) values('admin','admin','admin@atguigu.com');
select * from t_user;
3、编写数据库表对应的JavaBean对象
User
package com.chang.pojo;
public class User {
private Integer id;
private String username;
private String password;
private String email;
public User() {}
public User(Integer id, String username, String password, String email) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
}
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
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 getEmail() {return email;}
public void setEmail(String email) {this.email = email;}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
4、编写工具类 JdbcUtils
4.1、导入jar包(数据库和连接池需要)
放在WEB-INF文件夹的lib中
Druid首先是一个数据库连接池。Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。
4.2、在src源码目录下编写配置文件
jdbc.properties
username=root
password=mysql
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
4.3、在utils文件夹中编写JdbcUtils
实现与数据库的连接以及断开
JdbcUtils
调用DruidDataSources生成一个dataSource,将读取好的配置文件放到dataSource中,之后使用getConnection()进行连接,使用close()进行关闭。
package com.chang.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JdbcUtils {
private static DruidDataSource dataSource;
static {
Properties prop=new Properties();
// 读取 jdbc.properties属性配置文件
InputStream input=JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
// 从流中加载数据
prop.load(input);
} catch (IOException e) {
e.printStackTrace();
}
// 创建 数据库连接 池
try {
dataSource=(DruidDataSource) DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接池中的连接
* @return 如果返回null,说明获取连接失败<br/>有值就是获取连接成功
*/
public static Connection getConnection(){
Connection conn=null;
try {
conn=dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭连接,放回数据库连接池
* @param conn
*/
public static void close(Connection conn){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4.4、对JdbcUtils进行测试
在测试之前先将所需的包进行导入
- hamcrest-core-1.3.jar
- junit-4.12.jar
在test包中生曾JdbcUtilsTest类:IDEA中的快捷键是ctrl + shift + T
package com.chang.test;
import com.chang.utils.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;
import static org.junit.Assert.*;
public class JdbcUtilsTest {
@Test
public void testJdbcUtils() {
for(int i=0;i<100;i++){
Connection conn= JdbcUtils.getConnection();
System.out.println(conn);
JdbcUtils.close(conn);
}
}
}
5、编写BaseDao实现数据库的增删改查
5.1、导入DBUtils的jar包
commons-dbutils-1.3.jar
5.2、编写BaseDao
package com.chang.dao.impl;
import com.chang.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class BaseDao {
//使用DBUtils操作数据库
private QueryRunner queryRunner=new QueryRunner();
/**
* update() 方法用来执行:Insert\Update\Delete语句
*
* @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
*/
public int update(String sql,Object... args){
Connection conn= JdbcUtils.getConnection();
try {
return queryRunner.update(conn,sql,args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn);
}
return -1;
}
/**
* 查询返回一个javaBean的sql语句
*
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return 返回查找到的对象,若有多个,只返回第一个
*/
public <T> T queryForOne(Class<T> type,String sql,Object... args){
Connection conn=JdbcUtils.getConnection();
try {
return queryRunner.query(conn,sql,new BeanHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn);
}
return null;
}
/**
* 查询返回多个javaBean的sql语句
*
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return 返回查找到的对象列表
*/
public <T>List<T> queryForList(Class<T> type,String sql,Object...args){
Connection conn=JdbcUtils.getConnection();
try {
return queryRunner.query(conn,sql,new BeanListHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn);
}
return null;
}
/**
* 执行返回一行一列的sql语句
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @return 返回查找到的索引行(id),若有多个,只返回第一个
*/
public Object queryForStringValue(String sql,Object...args){
Connection conn=JdbcUtils.getConnection();
try {
return queryRunner.query(conn,sql,new ScalarHandler(),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn);
}
return null;
}
}
5.3、对BaseDao类进行测试
BaseDaoTest
package com.chang.test;
import com.chang.dao.impl.BaseDao;
import org.junit.Test;
import com.chang.pojo.User;
import java.util.List;
import static org.junit.Assert.*;
public class BaseDaoTest {
private BaseDao baseDao=new BaseDao();
@Test
public void update() {
String sql = "insert into t_user(`username`,`password`,`email`) values(?,?,?)";
int getLine = baseDao.update(sql, "xiaoming", "123456", "1234@qq.com");
System.out.println(getLine);
int getLine2 = baseDao.update(sql, "xiaohong", "123456", "4321@qq.com");
System.out.println(getLine2);
/*
1
1
*/
}
@Test
public void queryForOne() {
String sql="select * from t_user where password=?";
User user = baseDao.queryForOne(User.class, sql, "123456");
System.out.println(user);
/*
User{id=7, username='xiaoming', password='123456', email='1234@qq.com'}
*/
}
@Test
public void queryForList() {
String sql="select * from t_user where password=?";
List<User> users = baseDao.queryForList(User.class, sql, "123456");
System.out.println(users);
/*
[User{id=7, username='xiaoming', password='123456', email='1234@qq.com'},
User{id=8, username='xiaohong', password='123456', email='4321@qq.com'}]
*/
}
@Test
public void queryForStringValue() {
String sql="select * from t_user where password=?";
Object admin = baseDao.queryForStringValue(sql, "123456");
System.out.println(admin);
/*
7
*/
}
}
6、编写UserDao(对BaseDao封装)
6.1、接口:UserDao
package com.chang.dao;
import com.chang.pojo.User;
public interface UserDao {
/**
* 根据用户名查询用户信息
* @param username 用户名
* @return 如果返回 null,说明没有这个用户。反之亦然
*/
public User queryUserByUsername(String username);
/**
* 根据 用户名和密码查询用户信息
* @param username
* @param password
* @return 如果返回 null,说明用户名或密码错误,反之亦然
*/
public User queryUserByUsernameAndPassword(String username,String password);
/**
* 保存用户信息
* @param user
* @return 返回-1 表示操作失败,其他是 sql 语句影响的行数
*/
public int saveUser(User user);
}
6.2、实现类:UserDaoImpl
package com.chang.dao.impl;
import com.chang.dao.UserDao;
import com.chang.pojo.User;
public class UserDaoImpl extends BaseDao implements UserDao {
@Override
public User queryUserByUsername(String username) {
String sql="select * from t_user where username=?";
return queryForOne(User.class,sql,username);
}
@Override
public User queryUserByUsernameAndPassword(String username, String password) {
String sql="select * from t_user where username=? and password =?";
return queryForOne(User.class,sql,username,password);
}
@Override
public int saveUser(User user) {
String sql="insert into t_user(`username`,`password`,`email`) values(?,?,?)";
return update(sql,user.getUsername(),user.getPassword(),user.getEmail());
}
}
6.3、测试:UserDaoTest
package com.chang.test;
import com.chang.dao.UserDao;
import com.chang.dao.impl.UserDaoImpl;
import com.chang.pojo.User;
import org.junit.Test;
import static org.junit.Assert.*;
public class UserDaoTest {
UserDao userDao=new UserDaoImpl();
@Test
public void queryUserByUsername() {
User user = userDao.queryUserByUsername("xiaoming");
System.out.println(user);
if (userDao.queryUserByUsername("admin1234") == null ){
System.out.println("用户名可用!");
} else {
System.out.println("用户名已存在!");
}
/*
User{id=7, username='xiaoming', password='123456', email='1234@qq.com'}
用户名可用!
*/
}
@Test
public void queryUserByUsernameAndPassword() {
if ( userDao.queryUserByUsernameAndPassword("admin","admin1234") == null) {
System.out.println("用户名或密码错误,登录失败");
} else {
System.out.println("查询成功");
}
/*
用户名或密码错误,登录失败
*/
}
@Test
public void saveUser() {
System.out.println( userDao.saveUser(new User(null,"wzg168", "123456", "wzg168@qq.com")) );
/*
1
*/
}
}
7、编写UserService
7.1、接口:UserService
package com.chang.service;
import com.chang.pojo.User;
public interface UserService {
/**
* 注册用户
* @param user
*/
public void registUser(User user);
/**
* 登录
* @param user
* @return 如果返回 null,说明登录失败,返回有值,是登录成功
*/
public User login(User user);
/**
* 检查 用户名是否可用
* @param username
* @return 返回 true 表示用户名已存在,返回 false 表示用户名可用
*/
public boolean existsUsername(String username);
}
7.2、实现类:UserServiceImpl
package com.chang.service.impl;
import com.chang.dao.UserDao;
import com.chang.dao.impl.UserDaoImpl;
import com.chang.pojo.User;
import com.chang.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao=new UserDaoImpl();
@Override
public void registUser(User user) {
userDao.saveUser(user);
}
@Override
public User login(User user) {
return userDao.queryUserByUsernameAndPassword(user.getUsername(), user.getPassword());
}
@Override
public boolean existsUsername(String username) {
if (userDao.queryUserByUsername(username) == null) {
// 等于null,说明没查到,没查到表示可用
return false;
}
return true;
}
}
7.3、测试:UserSerciceTest
package com.chang.test;
import com.chang.pojo.User;
import com.chang.service.UserService;
import com.chang.service.impl.UserServiceImpl;
import org.junit.Test;
import static org.junit.Assert.*;
public class UserServiceTest {
UserService userService = new UserServiceImpl();
@Test
public void registUser() {
userService.registUser(new User(null, "bbj168", "666666", "bbj168@qq.com"));
userService.registUser(new User(null, "abc168", "666666", "abc168@qq.com"));
}
@Test
public void login() {
User wzg168 = userService.login(new User(null, "wzg168", "123456", null));
System.out.println(wzg168);
/*
User{id=9, username='wzg168', password='123456', email='wzg168@qq.com'}
*/
}
@Test
public void existsUsername() {
if (userService.existsUsername("wzg16888")) {
System.out.println("用户名已存在!");
} else {
System.out.println("用户名可用!");
}
/*
用户名可用!
*/
}
}
8、编写Web层
8.1、实现用户注册的功能
8.1.1、图解用户注册的流程:
8.1.2、修改 regist.html 和 regist_success.html 页面
1、添加 base 标签
<!--写 base 标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)
以下是几个修改的示例:
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
3、修改注册表单的提交地址和请求方式
8.1.3、编写 RegistServlet 程序
package com.chang.web;
import com.chang.pojo.User;
import com.chang.service.UserService;
import com.chang.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RegistServlet extends HttpServlet {
private UserService userService=new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取请求的参数
String username=req.getParameter("username");
String password=req.getParameter("password");
String email=req.getParameter("email");
String code=req.getParameter("code");
//2、检查 验证码是否正确 === 写死,要求验证码为:abcde
if("abcde".equalsIgnoreCase(code)){
//3、检查用户名是否可用
if(userService.existsUsername(username)){
System.out.println("用户名[" + username + "]已存在!");
//跳回注册页面
req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
}else {
//可用
//调用Sservice保存到数据库
userService.registUser(new User(null,username,password,email));
//跳到注册成功页面 regist_success.html
req.getRequestDispatcher("/pages/user/regist_success.html").forward(req,resp);
}
}else{
System.out.println("验证码[" + code + "]错误");
req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
}
}
}
8.1.4、在web.xml配置servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>RegistServlet</servlet-name>
<servlet-class>com.chang.web.RegistServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegistServlet</servlet-name>
<url-pattern>/registServlet</url-pattern>
</servlet-mapping>
</web-app>
8.2、实现用户登录的功能
8.2.1、图解用户登录
8.2.2、修改 login.html 页面和 login_success.html 页面
1、添加 base 标签
<!--写 base 标签,永远固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)
以下是几个修改的示例:
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
3、修改 login.html 表单的提交地址和请求方式
8.2.3、编写LoginServlet程序
package com.chang.web;
import com.chang.pojo.User;
import com.chang.service.UserService;
import com.chang.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取请求的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 调用 userService.login()登录处理业务
User loginUser = userService.login(new User(null, username, password, null));
// 如果等于null,说明登录 失败!
if (loginUser == null) {
// 跳回登录页面
req.getRequestDispatcher("/pages/user/login.html").forward(req, resp);
} else {
// 登录 成功
//跳到成功页面login_success.html
req.getRequestDispatcher("/pages/user/login_success.html").forward(req, resp);
}
}
}
8.2.4、在web.xml配置servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>RegistServlet</servlet-name>
<servlet-class>com.chang.web.RegistServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegistServlet</servlet-name>
<url-pattern>/registServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.chang.web.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/loginServlet</url-pattern>
</servlet-mapping>
</web-app>