分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。
web 层 | com.ydgk.web/servlet/controller | |
service 层 | com.ydgk.service | Service 接口包 |
com.ydgk.service.impl | Service 接口实现类 | |
dao 持久层 | com.ydgk.dao | Dao 接口包 |
com.ydgk.dao.impl | Dao 接口实现类 | |
实体bean 对象 | com.ydgk.pojo/entity/domain/bean | JavaBean 类 |
测试包 | com.ydgk.test/junit | |
工具类 | com.ydgk.utils |
搭建书城项目开发环境:
创建我们项目的包结构:
1、创建书城需要的数据库和表
2、编写数据库表对应的JavaBean对象
package com.bigdata.bean;
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 + '\'' +
'}';
}
}
3、编写工具类JdbcUtils
3.1 、导入需要的jar 包(数据库和连接池需要):
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
以下是测试需要:
hamcrest-core-1.3.jar
junit-4.12.jar
3.2 在src 源码目录下编写jdbc.properties 属性配置文件:
username=root password=021211 url=jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true driverClassName=com.mysql.jdbc.Driver initialSize=5 maxActive=10
3.3、编写JdbcUtils 工具类:
设置一个数据源的工具类,此类用于得到数据源(DruidDataSource)和数据库连接对象(Connection)
package com.bigdata.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JdbcUtils {
private static DruidDataSource dataSource;
static {
try {
Properties properties = new Properties();
// 读取 jdbc.properties 属性配置文件
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
properties.load(inputStream);
// 创建 数据库连接池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//额外加一个获取数据源方法
public static DruidDataSource getDataSource(){
return dataSource;
}
/**
*获取数据库连接池中的连接
*@return 如果返回null,说明获取连接失败<br/>有值就是获取连接成功
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (Exception e) { e.printStackTrace();
}
return conn;
}
/**
*关闭连接,放回数据库连接池
*@param conn
*/
public static void close(Connection conn){
if (conn != null) {
try {
conn.close();
} catch (SQLException e) { e.printStackTrace();
}
}
}
}
3.4JbUtil测试类
主要为了测试本项目与数据库是否正确连接
package com.bigdata.test;
import com.bigdata.utils.JdbcUtils;
import org.junit.Test;
import java.sql.Connection;
public class JdbcUtilsTest {
@Test
public void testJdbcUtils(){
for (int i = 0; i < 100; i++){
Connection connection = JdbcUtils.getConnection();
System.out.println(connection);
JdbcUtils.close(connection);
}
}
}
结果为
、
4、编写BaseDao
封装一些数据库操作需要的数据源,连接驱动等方法
package com.bigdata.utils;
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.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* 定义一个用来被继承的对数据库进行基本操作的Dao
* @param <T> 泛型 -> bean类型
*/
public abstract class BaseDao<T>{
private QueryRunner queryRunner ;
// 定义一个变量来接收泛型的类型
private Class<T> type;
// 获取T的Class对象,获取泛型的类型,泛型是在被子类继承时才确定
public BaseDao() {
queryRunner = new QueryRunner(JdbcUtils.getDataSource());
// 获取子类的类型
Class clazz = this.getClass();
// 获取父类的类型
// getGenericSuperclass()用来获取当前类的父类的类型
// ParameterizedType表示的是带泛型的类型
ParameterizedType parameterizedType =
(ParameterizedType) clazz.getGenericSuperclass();
// 获取具体的泛型类型 getActualTypeArguments获取具体的泛型的类型
// 这个方法会返回一个Type的数组
Type[] types = parameterizedType.getActualTypeArguments();
// 获取具体的泛型的类型·
this.type = (Class<T>) types[0];
}
/**
* 获取所有对象
*
* @param sql
* @param params
* @return
*/
public List<T> getBeanList(Connection conn, String sql, Object... params) {
List<T> list = null;
try {
list = queryRunner.query(conn, sql, new BeanListHandler<T>(type), params);
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
/**
* 获取一个对象
*
* @param sql
* @param params
* @return
*/
public T getBean(Connection conn,String sql, Object... params) {
T t = null;
try { //type == String.class
t = queryRunner.query(conn, sql, new BeanHandler<T>(type), params);
} catch (SQLException e) {
e.printStackTrace();
}
return t;
}
/**
* 通用的增删改操作(事务有关)
*
* @param sql
* @param params
* @return
*/
public int update(Connection conn, String sql, Object... params) {
int count = 0;
try {
count = queryRunner.update(conn, sql, params);
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
/**
* 通用的增删改操作(没有办法使用事务)
*
* @param sql
* @param params
* @return
*/
public int update(String sql, Object... params) {
int count = 0;
try {
count = queryRunner.update(sql, params);
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
/**
* 添加数据后返回主键id(事务有关)
* @param sql
* @param params
* @return
*/
public int insert(Connection conn ,String sql, Object... params){
try {
return queryRunner.insert(conn,sql , new ScalarHandler<BigInteger>() , params).intValue();
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
/**
* 添加数据后返回主键id(没有办法使用事务)
* @param sql
* @param params
* @return
*/
public int insert(String sql, Object... params){
try {
return queryRunner.insert(sql , new ScalarHandler<Long>() , params).intValue();
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
/**
* 获取单行单列值得方法,专门用来执行像 select count(*)...这样的sql语句
*
* @param sql
* @param params
* @return
*/
public int getValue(Connection conn,String sql, Object... params) {
int count = 0;
try {
// 调用queryRunner的query方法获取一个单一的值
count = queryRunner.query(conn, sql, new ScalarHandler<Long>(), params).intValue();
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
}
5、编写UserDao 和测试
UserDao接口:
package com.bigdata.dao;
import com.bigdata.bean.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);
}
UserDaoImpl实现类:
该类实现对数据库的一些操作(增删改查)
package com.bigdata.dao.impl;
import com.bigdata.bean.User;
import com.bigdata.dao.UserDao;
import com.bigdata.utils.BaseDao;
public class UserDaoImpl extends BaseDao<User> implements UserDao {
@Override
public User queryUserByUsername(String username) {
return getBean("select * from t_user where username = ?",username);
}
@Override
public User queryUserByUsernameAndPassword(String username, String password) {
return getBean("select * from t_user where username = ? and password = ?",username,password);
}
@Override
public int saveUser(User user) {
return insert("insert into t_user values(null,?,?,?)",
user.getUsername(),user.getPassword(),user.getEmail());
}
}
测试UserDao中的方法是否可以正常运行
6、编写UserService接口 与 UserServiceImpl实现类
UserService接口定义三个方法,分别是注册、登录、判断用户是否存在。
package com.bigdata.service;
import com.bigdata.bean.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);
}
该类实现接口,首先实例化UserDaoImpl,方法中调用UserDaoImpl类中的一些方法,也就是对数据库进行操作的方法。
package com.bigdata.service.impl;
import com.bigdata.bean.User;
import com.bigdata.dao.UserDao;
import com.bigdata.dao.impl.UserDaoImpl;
import com.bigdata.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) {
return userDao.queryUserByUsername(username) != null;
}
}
service层
service层就是业务层,在我们开发项目中,有很多复杂的业务功能,并不是单纯的crud,所以我们会创建一个业务层实现项目中复杂的业务功能,并且在此层中完成事务的操作。事务:要么成功都成功,要么失败都失败
dao层就是用来完成对数据库的crud,但是一个项目并不是只有crud所以我们需要业务层来完成复杂的功能。
比如:
转账
购物车
下订单
...
就不是我们在dao层使用crud能完成的
具体的业务层使用我们后期会在项目中来体现
7、编写web层
首先实现的是注册功能
1、添加base标签
<base href="http://localhost:8080/book/">
web前端页面的注册登录中一定会有提交表单
表单提交中的action就得是Servlet包下的类名,因为我们需要servlet技术将客户端的数据回显到后端,利用HttpServletRequest类中的getParameter()方法获取参数。得到用户名密码邮箱等,判断该用户名是否在数据库中存在,如果不存在就执行添加语句。添加成功使用
getRequestDispatcher()方法 获取请求转发对象,跳转到注册成功页面。
在Servlet包下面创建RegistServlet类,该类处理客户端发来的请求(也就是注册的信息)。
首先记得加上注解,并且该类要继承HttpServlet类。详情见前面发布的文章Servlet技术篇。
对HttpServlet类中的service方法进行重写。
package com.bigdata.servlet;
import com.bigdata.bean.User;
import com.bigdata.service.UserService;
import com.bigdata.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/RegistServlet")
public class RegistServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取到我们提交上来的用户名,邮箱,密码等。
//1、post会有乱码问题,先解决一下乱码
req.setCharacterEncoding("utf-8");
//2、获取表单提交上来的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
//创建一个用户业务层对象 userService
UserService userService = new UserServiceImpl();
//3、判断一下验证是不是正确的
if("1234".equals(code)){
//返回true,表示用户已存在,返回false
if (userService.existsUsername(username)){
req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
}else {
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setEmail(email);
//因为eregistUser方法需要传入的参数是一个user对象,所以我们创建一个user对象并将表单提交上来的参数都保存到User对象中
//这里调用userServiceImpl中的registUser方法,即注册方法,然后该类中的此方法是调用了UserDao类中的saveUser方法,sql语句添加到数据库中
userService.registUser(user);
//如果注册成功,就跳转到成功网页
req.getRequestDispatcher("/pages/user/regist_success.html").forward(req,resp);
}
}else{
//通过服务器转发的方式跳回到regist.html网页,因为验证不正确
req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
}
}
}
本book商城项目还未涉及到验证码功能。只是简单的三层架构,帮助大家更好的理解JavaEE项目。且重点在于后端,web代码不完全。可以简单点,html代码中只要有form表单提交,action不要写错验证数据库是否正确添加就行。
至此,完整的项目结构如下