到这里,你可能会觉得这些朋友的问题很简单,分层嘛,不就是将具有不同职责的组件分离开来,组成一套层内部高聚合,层与层之间低耦合的软件系统吗?不错!这是分层的目标。但是,我们应该如何分层呢?
注意图中打虚线的“基础结构层”,从实践的表现上来看,这部分内容可能就是一些帮助类,比如 SQLHelper之类的,也可能是一些工具类,比如TextUtility之类。这些东西可以被其它各层所访问。而基于分层的概念,表现层只能跟业务逻辑层打交道,而业务逻辑层在数据持久化方面的操作,则依赖于数据访问层。表现层对数据访问层的内容一无所知。
从领域驱动的角度看,这种分层的方式有一定的弊端。首先,为各个层面提供服务的“基础结构层”的职责比较紊乱,它可以是纯粹的技术框架,也可以包含或处理一定的业务逻辑,这样一来,业务逻辑层与“基础结构层”之间就会存在依赖关系;其次,这种结构过分地突出了“数据访问”的地位,把“数据访问”与 “业务逻辑”放在了等同的地位,这也难怪很多软件人员一上来就问:“我的数据表该如何设计?”
从上图还可以看到,表现层与应用层之间是通过数据传输对象(DTO)进行交互的,数据传输对象是没有行为的POCO对象,它的目的只是为了对领域对象进行数据封装,实现层与层之间的数据传递。为何不能直接将领域对象用于数据传递?因为领域对象更注重领域,而DTO更注重数据。不仅如此,由于“富领域模型”的特点,这样做会直接将领域对象的行为暴露给表现层。
我们首先在mysql数据库中进行建表插数据操作
主要包含两张表:tbl_user表和tbl_address表
tbl_user表中主要储存用户信息,包括id,用户名,密码,邮箱
tbl_address表中主要储存用户的地址信息,使用user_id作为外键与tbl_user表连接起来。
然后,在表中插入一些测试信息:
CREATE TABLE tbl_user(
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
uname VARCHAR(50) NOT NULL DEFAULT '',
upwd VARCHAR(50) NOT NULL DEFAULT '',
email VARCHAR(50) DEFAULT '',
PRIMARY KEY(id))
ENGINE=INNODB
DEFAULT CHARSET=utf8;
CREATE TABLE tbl_address(
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
city VARCHAR(20) DEFAULT NULL,
country VARCHAR(20) DEFAULT NULL,
user_id INT(11) UNSIGNED NOT NULL,
PRIMARY KEY(id))
ENGINE=INNODB
DEFAULT CHARSET=utf8;
INSERT INTO tbl_user(id,uname,upwd,email)
VALUES
(1,'xiaoming','123456','xiaoming@qq.com'),
(2,'xiaozhang','123456','xiaozhang@qq.com');
INSERT INTO tbl_address(city,country,user_id)
VALUES('beijing','China',1);
INSERT INTO tbl_address(city,country,user_id)
VALUES('tianjing','China',2);
数据库创建完成之后我们就在项目中创建对应的DTO类
首先,在项目中创建包:entity
在包中创建IdEnity类:IdEntity封装了所有的表中的非业务的主键id
public abstract class IdEnity {
protected long id;
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
}
接下来我们创建User类,这个类继承自IdEnity类。
public class User extends IdEnity {
private String uname;
private String upwd;
private String email;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpwd() {
return upwd;
}
public void setUpwd(String upwd) {
this.upwd = upwd;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public long getId() {
return super.getId();
}
@Override
public void setId(long id) {
super.setId(id);
}
@Override
public String toString() {
return "User{" +
"uname='" + uname + '\'' +
", upwd='" + upwd + '\'' +
", email='" + email + '\'' +
", id='"+id+'\''+
'}';
}
}
Address类同样需要继承IdEnity
public class Address extends IdEnity{
private String city;
private String country;
private long userId;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
", country='" + country + '\'' +
", userId=" + userId +'\'' +
", id='"+id+'\''+
'}';
}
}
将数据库创建好之后,接下来便是要使我们的项目可以连接到数据库,
因为我们使用的是mysql数据库,所以我们下载mysql对应的jdbc驱动。
下载地址https://dev.mysql.com/downloads/connector/j/
下载好之后将jar包文件添加到项目中去
然后我们便可以开发连接类来获取数据库的连接。
创建一个新包:util,在包中创建ConnectionFactory类
public class ConnectionFactory {
private static String driver;
private static String dburl;
private static String name;
private static String password;
//声明ConnectionFactory类型对象
private static final ConnectionFactory factory=new ConnectionFactory();
//用于保存数据库连接
private Connection conn;
/**
* 使用静态代码块从属性文件中读取配置信息
* 静态代码快用于初始化类,用于给类进行赋值
* 当jvm加载类的时候会执行其中的静态代码块,只会执行一次
*/
static {
//定义Properties类
Properties prop=new Properties();
try {
//读取配置文件信息
//首先获取当前类的类加载器,
// 然后使用类加载器的getResourceAsStream方法将文件读取到一个输入流中
InputStream in=ConnectionFactory.class.getClassLoader().
getResourceAsStream("dbconfig.properties");
//使用load方法读取属性列表
prop.load(in);
} catch (IOException e) {
System.out.println("========配置文件读取错误========");
}
//将读取到的值赋值给成员变量
driver=prop.getProperty("driver");
dburl=prop.getProperty("dburl");
name=prop.getProperty("name");
password=prop.getProperty("password");
}
/**
* 构造函数
*/
private ConnectionFactory(){
}
/**
* 使用单例模式,保证运行期间只有一个ConnectionFactory对象存在
* @return
*/
public static ConnectionFactory getInstance(){
return factory;
}
/**
* 获取数据库连接
* @return
*/
public Connection makeConnection(){
try {
Class.forName(driver);
conn= DriverManager.getConnection(dburl,name,password);
}catch (Exception e){
e.printStackTrace();
}
return conn;
}
}
在这里我们使用到了配置文件来储存我们的数据库连接信息
在项目src文件夹下创建一个dbconfig.porperties配置文件来存储我们连接数据库的配置参数
配置文件中的信息如下:
driver=com.mysql.jdbc.Driver
dburl=jdbc\:mysql\://localhost\:3306/jsp_db
name=wangxin
password=*******(对应自己用户名的密码)
接下来我们实现DAO类的编写
首先我们新建一个dao包,在包中添加UserDao类
UserDao类主要定义方法的接口,使用面向接口开发来实现这些功能。
public interface UserDao {
public void save(Connection conn, User user)throws SQLException;
public void update(Connection conn, long id, User user)throws SQLException;
public void delete(Connection conn, User user)throws SQLException;
public ResultSet get(Connection conn, User user)throws SQLException;
}
在dao包中在新建一个impl包,用于存放实现UserDao接口的实现类
子啊impl包中新建UserDaoImpl类实现了UserDao接口,并在类中实现了接口中定义的方法
public class UserDaoImpl implements UserDao{
/**
* 添加用户信息
* @param conn
* @param user
* @throws SQLException
*/
@Override
public void save(Connection conn, User user) throws SQLException {
PreparedStatement pstm
=conn.prepareStatement("INSERT INTO tbl_user(uname,upwd,email) VALUES (?,?,?)");
pstm.setString(1,user.getUname());
pstm.setString(2,user.getUpwd());
pstm.setString(3,user.getEmail());
pstm.execute();
}
/**
* 更新用户信息
* @param conn
* @param id
* @param user
* @throws SQLException
*/
@Override
public void update(Connection conn, long id, User user) throws SQLException {
PreparedStatement pstm=
conn.prepareStatement("UPDATE tbl_user SET uname=?,upwd=?,email=? WHERE id=?");
pstm.setString(1,user.getUname());
pstm.setString(2,user.getUpwd());
pstm.setString(3,user.getEmail());
pstm.setLong(4,id);
pstm.execute();
}
/**
* 删除用户信息
* @param conn
* @param user
* @throws SQLException
*/
@Override
public void delete(Connection conn, User user) throws SQLException {
PreparedStatement pstm=
conn.prepareStatement("DELETE FROM tbl_user WHERE id=?");
pstm.setLong(1,user.getId());
pstm.execute();
}
/**
* 获取查询结果
* @param conn
* @param user
* @return
* @throws SQLException
*/
@Override
public ResultSet get(Connection conn, User user) throws SQLException {
PreparedStatement ps=conn.prepareStatement("SELECT * FROM tbl_user WHERE uname=? AND upwd=?");
ps.setString(1,user.getUname());
ps.setString(2,user.getUpwd());
return ps.executeQuery();
}
}
在后续的数据操作中我们便可以使用此类中的函数来执行对数据库的数据操作。
接下来我们创建服务类来执行校验用户名和密码的服务操作
首先创建一个service包,在包中创建一个类CheckUserService类
public class CheckUserService {
private UserDao userDao=new UserDaoImpl();
public boolean check(User user){
Connection conn=null;
try{
//关闭自动提交
conn= ConnectionFactory.getInstance().makeConnection();
conn.setAutoCommit(false);
//查询是否在数据库中有对应的信息
ResultSet rs=userDao.get(conn,user);
if (rs.next()){
return true;
}
}catch (Exception e){
e.printStackTrace();
try {
conn.rollback();
}catch (Exception e1){
e1.printStackTrace();
}
}
finally {
try{
conn.close();
}catch (Exception e2){
e2.printStackTrace();
}
}
return false;
}
}
接下来我们编写Servlet的操作代码,主要是将jsp页面传递过来的表单数据进行封装为DTO类,并使用CheckServletService来对数据进行检查。
public class CheckServlet extends HttpServlet {
private CheckUserService checkUserService=new CheckUserService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uname=request.getParameter("uname");
String upwd=request.getParameter("upwd");
RequestDispatcher rd=null;
String forword=null;
//判断用户名和密码是否为空
if (uname==null||upwd==null){
request.setAttribute("msg","用户名或者密码为空");
rd=request.getRequestDispatcher("/15/error.jsp");
rd.forward(request,response);
}else{
User user=new User();
user.setUname(uname);
user.setUpwd(upwd);
boolean bool=checkUserService.check(user);
if (bool){
forword="/15/success.jsp";
}else{
request.setAttribute("msg","用户名或者密码输入错误,请重试!");
forword="/15/error.jsp";
}
rd=request.getRequestDispatcher(forword);
rd.forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//以后开发在doGet方法中直接调用doPost方法
doPost(request,response);
}
}
这样,我们的程序便优化完成,成为了一个具有数据库的登录程序。
项目下载地址:https://github.com/icaruswang/StudyJsp