1、JavaEE三层架构

6、Web项目架构与数据库使用 - 图1

客户端提交发送到后端的数据,会先在Web层获取并封装成Bean对象,然后调用Service层的方法处理业务,再调用Dao层的方法与数据库交互实现数据的持久化存储

分层的目的是为了解耦,所谓的解耦就是降低代码的耦合度,方便项目后期的维护与升级。

6、Web项目架构与数据库使用 - 图2

在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

6、Web项目架构与数据库使用 - 图3

2、创建项目需要的数据库和表

在终端的MySQL中输入如下命令:

  1. drop database if exists book;
  2. create database book;
  3. use book;
  4. create table t_user(
  5. `id` int primary key auto_increment,
  6. `username` varchar(20) not null unique,
  7. `password` varchar(32) not null,
  8. `email` varchar(200)
  9. );
  10. insert into t_user(`username`,`password`,`email`) values('admin','admin','admin@atguigu.com');
  11. select * from t_user;

6、Web项目架构与数据库使用 - 图4

3、编写数据库表对应的JavaBean对象

User

  1. package com.chang.pojo;
  2. public class User {
  3. private Integer id;
  4. private String username;
  5. private String password;
  6. private String email;
  7. public User() {}
  8. public User(Integer id, String username, String password, String email) {
  9. this.id = id;
  10. this.username = username;
  11. this.password = password;
  12. this.email = email;
  13. }
  14. public Integer getId() {return id;}
  15. public void setId(Integer id) {this.id = id;}
  16. public String getUsername() {return username;}
  17. public void setUsername(String username) {this.username = username;}
  18. public String getPassword() {return password;}
  19. public void setPassword(String password) {this.password = password;}
  20. public String getEmail() {return email;}
  21. public void setEmail(String email) {this.email = email;}
  22. @Override
  23. public String toString() {
  24. return "User{" +
  25. "id=" + id +
  26. ", username='" + username + '\'' +
  27. ", password='" + password + '\'' +
  28. ", email='" + email + '\'' +
  29. '}';
  30. }
  31. }

4、编写工具类 JdbcUtils

4.1、导入jar包(数据库和连接池需要)

放在WEB-INF文件夹的lib中

Druid首先是一个数据库连接池。Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。

6、Web项目架构与数据库使用 - 图5

4.2、在src源码目录下编写配置文件

jdbc.properties

  1. username=root
  2. password=mysql
  3. url=jdbc:mysql://localhost:3306/book
  4. driverClassName=com.mysql.jdbc.Driver
  5. initialSize=5
  6. maxActive=10

4.3、在utils文件夹中编写JdbcUtils

实现与数据库的连接以及断开

JdbcUtils

调用DruidDataSources生成一个dataSource,将读取好的配置文件放到dataSource中,之后使用getConnection()进行连接,使用close()进行关闭。

  1. package com.chang.utils;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import com.alibaba.druid.pool.DruidDataSourceFactory;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.sql.Connection;
  7. import java.sql.SQLException;
  8. import java.util.Properties;
  9. public class JdbcUtils {
  10. private static DruidDataSource dataSource;
  11. static {
  12. Properties prop=new Properties();
  13. // 读取 jdbc.properties属性配置文件
  14. InputStream input=JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
  15. try {
  16. // 从流中加载数据
  17. prop.load(input);
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. // 创建 数据库连接 池
  22. try {
  23. dataSource=(DruidDataSource) DruidDataSourceFactory.createDataSource(prop);
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. /**
  29. * 获取数据库连接池中的连接
  30. * @return 如果返回null,说明获取连接失败<br/>有值就是获取连接成功
  31. */
  32. public static Connection getConnection(){
  33. Connection conn=null;
  34. try {
  35. conn=dataSource.getConnection();
  36. } catch (SQLException e) {
  37. e.printStackTrace();
  38. }
  39. return conn;
  40. }
  41. /**
  42. * 关闭连接,放回数据库连接池
  43. * @param conn
  44. */
  45. public static void close(Connection conn){
  46. if(conn!=null){
  47. try {
  48. conn.close();
  49. } catch (SQLException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. }
  54. }

4.4、对JdbcUtils进行测试

在测试之前先将所需的包进行导入

  • hamcrest-core-1.3.jar
  • junit-4.12.jar

在test包中生曾JdbcUtilsTest类:IDEA中的快捷键是ctrl + shift + T

  1. package com.chang.test;
  2. import com.chang.utils.JdbcUtils;
  3. import org.junit.Test;
  4. import java.sql.Connection;
  5. import static org.junit.Assert.*;
  6. public class JdbcUtilsTest {
  7. @Test
  8. public void testJdbcUtils() {
  9. for(int i=0;i<100;i++){
  10. Connection conn= JdbcUtils.getConnection();
  11. System.out.println(conn);
  12. JdbcUtils.close(conn);
  13. }
  14. }
  15. }

5、编写BaseDao实现数据库的增删改查

5.1、导入DBUtils的jar包

  1. commons-dbutils-1.3.jar

5.2、编写BaseDao

  1. package com.chang.dao.impl;
  2. import com.chang.utils.JdbcUtils;
  3. import org.apache.commons.dbutils.QueryRunner;
  4. import org.apache.commons.dbutils.handlers.BeanHandler;
  5. import org.apache.commons.dbutils.handlers.BeanListHandler;
  6. import org.apache.commons.dbutils.handlers.ScalarHandler;
  7. import java.sql.Connection;
  8. import java.sql.SQLException;
  9. import java.util.List;
  10. public class BaseDao {
  11. //使用DBUtils操作数据库
  12. private QueryRunner queryRunner=new QueryRunner();
  13. /**
  14. * update() 方法用来执行:Insert\Update\Delete语句
  15. *
  16. * @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
  17. */
  18. public int update(String sql,Object... args){
  19. Connection conn= JdbcUtils.getConnection();
  20. try {
  21. return queryRunner.update(conn,sql,args);
  22. } catch (SQLException e) {
  23. e.printStackTrace();
  24. }finally {
  25. JdbcUtils.close(conn);
  26. }
  27. return -1;
  28. }
  29. /**
  30. * 查询返回一个javaBean的sql语句
  31. *
  32. * @param type 返回的对象类型
  33. * @param sql 执行的sql语句
  34. * @param args sql对应的参数值
  35. * @param <T> 返回的类型的泛型
  36. * @return 返回查找到的对象,若有多个,只返回第一个
  37. */
  38. public <T> T queryForOne(Class<T> type,String sql,Object... args){
  39. Connection conn=JdbcUtils.getConnection();
  40. try {
  41. return queryRunner.query(conn,sql,new BeanHandler<T>(type),args);
  42. } catch (SQLException e) {
  43. e.printStackTrace();
  44. }finally {
  45. JdbcUtils.close(conn);
  46. }
  47. return null;
  48. }
  49. /**
  50. * 查询返回多个javaBean的sql语句
  51. *
  52. * @param type 返回的对象类型
  53. * @param sql 执行的sql语句
  54. * @param args sql对应的参数值
  55. * @param <T> 返回的类型的泛型
  56. * @return 返回查找到的对象列表
  57. */
  58. public <T>List<T> queryForList(Class<T> type,String sql,Object...args){
  59. Connection conn=JdbcUtils.getConnection();
  60. try {
  61. return queryRunner.query(conn,sql,new BeanListHandler<T>(type),args);
  62. } catch (SQLException e) {
  63. e.printStackTrace();
  64. }finally {
  65. JdbcUtils.close(conn);
  66. }
  67. return null;
  68. }
  69. /**
  70. * 执行返回一行一列的sql语句
  71. * @param sql 执行的sql语句
  72. * @param args sql对应的参数值
  73. * @return 返回查找到的索引行(id),若有多个,只返回第一个
  74. */
  75. public Object queryForStringValue(String sql,Object...args){
  76. Connection conn=JdbcUtils.getConnection();
  77. try {
  78. return queryRunner.query(conn,sql,new ScalarHandler(),args);
  79. } catch (SQLException e) {
  80. e.printStackTrace();
  81. }finally {
  82. JdbcUtils.close(conn);
  83. }
  84. return null;
  85. }
  86. }

5.3、对BaseDao类进行测试

BaseDaoTest

  1. package com.chang.test;
  2. import com.chang.dao.impl.BaseDao;
  3. import org.junit.Test;
  4. import com.chang.pojo.User;
  5. import java.util.List;
  6. import static org.junit.Assert.*;
  7. public class BaseDaoTest {
  8. private BaseDao baseDao=new BaseDao();
  9. @Test
  10. public void update() {
  11. String sql = "insert into t_user(`username`,`password`,`email`) values(?,?,?)";
  12. int getLine = baseDao.update(sql, "xiaoming", "123456", "1234@qq.com");
  13. System.out.println(getLine);
  14. int getLine2 = baseDao.update(sql, "xiaohong", "123456", "4321@qq.com");
  15. System.out.println(getLine2);
  16. /*
  17. 1
  18. 1
  19. */
  20. }
  21. @Test
  22. public void queryForOne() {
  23. String sql="select * from t_user where password=?";
  24. User user = baseDao.queryForOne(User.class, sql, "123456");
  25. System.out.println(user);
  26. /*
  27. User{id=7, username='xiaoming', password='123456', email='1234@qq.com'}
  28. */
  29. }
  30. @Test
  31. public void queryForList() {
  32. String sql="select * from t_user where password=?";
  33. List<User> users = baseDao.queryForList(User.class, sql, "123456");
  34. System.out.println(users);
  35. /*
  36. [User{id=7, username='xiaoming', password='123456', email='1234@qq.com'},
  37. User{id=8, username='xiaohong', password='123456', email='4321@qq.com'}]
  38. */
  39. }
  40. @Test
  41. public void queryForStringValue() {
  42. String sql="select * from t_user where password=?";
  43. Object admin = baseDao.queryForStringValue(sql, "123456");
  44. System.out.println(admin);
  45. /*
  46. 7
  47. */
  48. }
  49. }

6、Web项目架构与数据库使用 - 图6

6、编写UserDao(对BaseDao封装)

6.1、接口:UserDao

  1. package com.chang.dao;
  2. import com.chang.pojo.User;
  3. public interface UserDao {
  4. /**
  5. * 根据用户名查询用户信息
  6. * @param username 用户名
  7. * @return 如果返回 null,说明没有这个用户。反之亦然
  8. */
  9. public User queryUserByUsername(String username);
  10. /**
  11. * 根据 用户名和密码查询用户信息
  12. * @param username
  13. * @param password
  14. * @return 如果返回 null,说明用户名或密码错误,反之亦然
  15. */
  16. public User queryUserByUsernameAndPassword(String username,String password);
  17. /**
  18. * 保存用户信息
  19. * @param user
  20. * @return 返回-1 表示操作失败,其他是 sql 语句影响的行数
  21. */
  22. public int saveUser(User user);
  23. }

6.2、实现类:UserDaoImpl

  1. package com.chang.dao.impl;
  2. import com.chang.dao.UserDao;
  3. import com.chang.pojo.User;
  4. public class UserDaoImpl extends BaseDao implements UserDao {
  5. @Override
  6. public User queryUserByUsername(String username) {
  7. String sql="select * from t_user where username=?";
  8. return queryForOne(User.class,sql,username);
  9. }
  10. @Override
  11. public User queryUserByUsernameAndPassword(String username, String password) {
  12. String sql="select * from t_user where username=? and password =?";
  13. return queryForOne(User.class,sql,username,password);
  14. }
  15. @Override
  16. public int saveUser(User user) {
  17. String sql="insert into t_user(`username`,`password`,`email`) values(?,?,?)";
  18. return update(sql,user.getUsername(),user.getPassword(),user.getEmail());
  19. }
  20. }

6.3、测试:UserDaoTest

  1. package com.chang.test;
  2. import com.chang.dao.UserDao;
  3. import com.chang.dao.impl.UserDaoImpl;
  4. import com.chang.pojo.User;
  5. import org.junit.Test;
  6. import static org.junit.Assert.*;
  7. public class UserDaoTest {
  8. UserDao userDao=new UserDaoImpl();
  9. @Test
  10. public void queryUserByUsername() {
  11. User user = userDao.queryUserByUsername("xiaoming");
  12. System.out.println(user);
  13. if (userDao.queryUserByUsername("admin1234") == null ){
  14. System.out.println("用户名可用!");
  15. } else {
  16. System.out.println("用户名已存在!");
  17. }
  18. /*
  19. User{id=7, username='xiaoming', password='123456', email='1234@qq.com'}
  20. 用户名可用!
  21. */
  22. }
  23. @Test
  24. public void queryUserByUsernameAndPassword() {
  25. if ( userDao.queryUserByUsernameAndPassword("admin","admin1234") == null) {
  26. System.out.println("用户名或密码错误,登录失败");
  27. } else {
  28. System.out.println("查询成功");
  29. }
  30. /*
  31. 用户名或密码错误,登录失败
  32. */
  33. }
  34. @Test
  35. public void saveUser() {
  36. System.out.println( userDao.saveUser(new User(null,"wzg168", "123456", "wzg168@qq.com")) );
  37. /*
  38. 1
  39. */
  40. }
  41. }

7、编写UserService

7.1、接口:UserService

  1. package com.chang.service;
  2. import com.chang.pojo.User;
  3. public interface UserService {
  4. /**
  5. * 注册用户
  6. * @param user
  7. */
  8. public void registUser(User user);
  9. /**
  10. * 登录
  11. * @param user
  12. * @return 如果返回 null,说明登录失败,返回有值,是登录成功
  13. */
  14. public User login(User user);
  15. /**
  16. * 检查 用户名是否可用
  17. * @param username
  18. * @return 返回 true 表示用户名已存在,返回 false 表示用户名可用
  19. */
  20. public boolean existsUsername(String username);
  21. }

7.2、实现类:UserServiceImpl

  1. package com.chang.service.impl;
  2. import com.chang.dao.UserDao;
  3. import com.chang.dao.impl.UserDaoImpl;
  4. import com.chang.pojo.User;
  5. import com.chang.service.UserService;
  6. public class UserServiceImpl implements UserService {
  7. private UserDao userDao=new UserDaoImpl();
  8. @Override
  9. public void registUser(User user) {
  10. userDao.saveUser(user);
  11. }
  12. @Override
  13. public User login(User user) {
  14. return userDao.queryUserByUsernameAndPassword(user.getUsername(), user.getPassword());
  15. }
  16. @Override
  17. public boolean existsUsername(String username) {
  18. if (userDao.queryUserByUsername(username) == null) {
  19. // 等于null,说明没查到,没查到表示可用
  20. return false;
  21. }
  22. return true;
  23. }
  24. }

7.3、测试:UserSerciceTest

  1. package com.chang.test;
  2. import com.chang.pojo.User;
  3. import com.chang.service.UserService;
  4. import com.chang.service.impl.UserServiceImpl;
  5. import org.junit.Test;
  6. import static org.junit.Assert.*;
  7. public class UserServiceTest {
  8. UserService userService = new UserServiceImpl();
  9. @Test
  10. public void registUser() {
  11. userService.registUser(new User(null, "bbj168", "666666", "bbj168@qq.com"));
  12. userService.registUser(new User(null, "abc168", "666666", "abc168@qq.com"));
  13. }
  14. @Test
  15. public void login() {
  16. User wzg168 = userService.login(new User(null, "wzg168", "123456", null));
  17. System.out.println(wzg168);
  18. /*
  19. User{id=9, username='wzg168', password='123456', email='wzg168@qq.com'}
  20. */
  21. }
  22. @Test
  23. public void existsUsername() {
  24. if (userService.existsUsername("wzg16888")) {
  25. System.out.println("用户名已存在!");
  26. } else {
  27. System.out.println("用户名可用!");
  28. }
  29. /*
  30. 用户名可用!
  31. */
  32. }
  33. }

8、编写Web层

8.1、实现用户注册的功能

8.1.1、图解用户注册的流程:

6、Web项目架构与数据库使用 - 图7

8.1.2、修改 regist.html 和 regist_success.html 页面

1、添加 base 标签

  1. <!--写 base 标签,永远固定相对路径跳转的结果-->
  2. <base href="http://localhost:8080/book/">

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)

以下是几个修改的示例:

  1. <link type="text/css" rel="stylesheet" href="static/css/style.css" >
  2. <script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>

3、修改注册表单的提交地址和请求方式

6、Web项目架构与数据库使用 - 图8

8.1.3、编写 RegistServlet 程序

  1. package com.chang.web;
  2. import com.chang.pojo.User;
  3. import com.chang.service.UserService;
  4. import com.chang.service.impl.UserServiceImpl;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. public class RegistServlet extends HttpServlet {
  11. private UserService userService=new UserServiceImpl();
  12. @Override
  13. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  14. // 1、获取请求的参数
  15. String username=req.getParameter("username");
  16. String password=req.getParameter("password");
  17. String email=req.getParameter("email");
  18. String code=req.getParameter("code");
  19. //2、检查 验证码是否正确 === 写死,要求验证码为:abcde
  20. if("abcde".equalsIgnoreCase(code)){
  21. //3、检查用户名是否可用
  22. if(userService.existsUsername(username)){
  23. System.out.println("用户名[" + username + "]已存在!");
  24. //跳回注册页面
  25. req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
  26. }else {
  27. //可用
  28. //调用Sservice保存到数据库
  29. userService.registUser(new User(null,username,password,email));
  30. //跳到注册成功页面 regist_success.html
  31. req.getRequestDispatcher("/pages/user/regist_success.html").forward(req,resp);
  32. }
  33. }else{
  34. System.out.println("验证码[" + code + "]错误");
  35. req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
  36. }
  37. }
  38. }

8.1.4、在web.xml配置servlet

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <servlet>
  7. <servlet-name>RegistServlet</servlet-name>
  8. <servlet-class>com.chang.web.RegistServlet</servlet-class>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>RegistServlet</servlet-name>
  12. <url-pattern>/registServlet</url-pattern>
  13. </servlet-mapping>
  14. </web-app>

8.2、实现用户登录的功能

8.2.1、图解用户登录

6、Web项目架构与数据库使用 - 图9

8.2.2、修改 login.html 页面和 login_success.html 页面

1、添加 base 标签

  1. <!--写 base 标签,永远固定相对路径跳转的结果-->
  2. <base href="http://localhost:8080/book/">

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)

以下是几个修改的示例:

  1. <link type="text/css" rel="stylesheet" href="static/css/style.css" >
  2. <script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>

3、修改 login.html 表单的提交地址和请求方式

6、Web项目架构与数据库使用 - 图10

8.2.3、编写LoginServlet程序

  1. package com.chang.web;
  2. import com.chang.pojo.User;
  3. import com.chang.service.UserService;
  4. import com.chang.service.impl.UserServiceImpl;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. public class LoginServlet extends HttpServlet {
  11. private UserService userService = new UserServiceImpl();
  12. @Override
  13. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  14. // 1、获取请求的参数
  15. String username = req.getParameter("username");
  16. String password = req.getParameter("password");
  17. // 调用 userService.login()登录处理业务
  18. User loginUser = userService.login(new User(null, username, password, null));
  19. // 如果等于null,说明登录 失败!
  20. if (loginUser == null) {
  21. // 跳回登录页面
  22. req.getRequestDispatcher("/pages/user/login.html").forward(req, resp);
  23. } else {
  24. // 登录 成功
  25. //跳到成功页面login_success.html
  26. req.getRequestDispatcher("/pages/user/login_success.html").forward(req, resp);
  27. }
  28. }
  29. }

8.2.4、在web.xml配置servlet

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <servlet>
  7. <servlet-name>RegistServlet</servlet-name>
  8. <servlet-class>com.chang.web.RegistServlet</servlet-class>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>RegistServlet</servlet-name>
  12. <url-pattern>/registServlet</url-pattern>
  13. </servlet-mapping>
  14. <servlet>
  15. <servlet-name>LoginServlet</servlet-name>
  16. <servlet-class>com.chang.web.LoginServlet</servlet-class>
  17. </servlet>
  18. <servlet-mapping>
  19. <servlet-name>LoginServlet</servlet-name>
  20. <url-pattern>/loginServlet</url-pattern>
  21. </servlet-mapping>
  22. </web-app>