第5章
当当书城Spring整合JDBC
5.1 当当书城基本功能
本节为当当书城项目介绍。它分为前台和后台两部分,前台是普通用户和游客访问,后台为系统管理员访问。
前台基本功能有主页图书推荐、图书详情显示、购物车管理、图书付款、我的订单等。
后台基本功能有用户订单查询、图书上架、图书下架、图书促销管理等。
用户的角色共分为四种:游客、注册用户、会员(会员为预留用户)、管理员。游客为非注册用户,可以浏览主页和图书详情;注册用户登录后可以添加商品到购物车,并付款结算;会员也可称为VIP用户,可以进入会员专区;管理员进入系统后台,可以浏览用户订单、上架图书、下架图书、修改图书价格等。
本章项目所有代码,参见附件中的DangDang(基础版)和DangSpring源码包。
5.1.1 项目开发环境
项目环境是以Jakarta EE 9+为基础,具体为JDK17、Tomcat 10.1、Sevlet 5.0、Spring 6.0。数据库采用MySQL 8或Oracle 11。IDE为Eclipse,选择2022-12-eclipse-inst-jre-win64或更高版本均可。数据库的设计工具使用Power Designer 15。逻辑设计工具使用Rational Rose。
5.1.2 表结构设计
当当书城项目同时支持两套数据库,即?MySQL?和?Oracle,还可以扩展支持其他数 据库。
书城项目的主要目的是在实际项目中强化前面的知识点,应该尽量覆盖更多知识点,而不是使功能更完善,因此设计上不能过于复杂,重复功能都尽量省略了,这与大型的实际企业级项目是有很大区别的。
当当书城的MySQL表结构见图5-1。
图5-1?当当书城表结构
表设计需注意以下几点。
(1)Oracle中使用varchar2类型,MySQL中使用varchar类型。
(2)Oracle中的整型和浮点型推荐使用number,MySQL中使用int和double。
(3)Oracle与MySQL库的所有字段名称、字段数量相同。
(4)“订单”表的主键为字符型,需要保证订单编号在高并发环境的唯一性。
(5)??“订单明细”表的主键为长整型,Oracle采用sequence,MySQL使用auto_increment。一个用户可以有多个订单,每个订单有多个订单明细。
(6)“图书”表用isbn作为主键,即每个isbn只存储一条记录,不是每本书一条记录。这与大型设备的管理模式不同,大型设备如汽车,必须是一辆车存一条记录。
(7)“主页推荐图书”与“图书”表为一对一关系,即从所有图书中挑选部分图书,作为主页推荐,而且可以设置推荐图书的显示顺序。
(8)在MySQL中创建数据库bk,然后创建表。
create database bk; //创建数据库,名字为bk
use bk; //打开库
//其他创建表的SQL语句,参见本书配套资源
5.1.3 当当书城原型
当当书城部分功能的原型样式见图5-2~图5-9。
图5-2?书城主页
图5-3?图书详情页
图5-4?用户登录页
图5-5?购物车页
图5-6?商品结算页
图5-7?付款页
图5-8?我的订单页
图5-9?图书上架页
5.2 Spring整合JDBC实战
本节基于当当书城基础版(使用Servlet+JSP+JDBC+Spring实现,参见本书配套资源),使用Spring框架的IoC、AOP和持久层整合技术,实现当当书城逻辑层和持久层的代码管理。
5.2.1 导包
配置pom.xml文件,导入相应的包。主要依赖包的配置如下:
jakarta.servlet
jakarta.servlet-api
5.0.0
org.glassfish.web
jakarta.servlet.jsp
3.0.0
org.glassfish.web
jakarta.servlet.jsp.jstl
3.0.1
jakarta.servlet.jsp.jstl
jakarta.servlet.jsp.jstl-api
3.0.0
org.springframework
spring-context
6.0.3
org.springframework
spring-jdbc
6.0.3
mysql
mysql-connector-java
8.0.27
org.apache.logging.log4j
log4j-slf4j-impl
2.13.3
5.2.2 Spring配置文件
在当当书城项目的src下,新建beans.xml文件,此为Spring的核心配置文件。配置信息操作如下。
(1)配置schema。
(2)配置数据源。
(3)配置事务管理器。
(4)配置Spring Bean的扫描位置。
5.2.3 封装BaseDao
BaseDao是所有持久层类的抽象父类,在此类中注入数据源,同时封装获取数据库连接和关闭连接的操作。下面讲述操作步骤。
(1)BaseDao继承JdbcDaoSupport工具类。在JdbcDaoSupport中封装JdbcTemplate和DataSourceUtils(参见4.5.3节)。
public abstract class BaseDao extends JdbcDaoSupport{ }
(2)注入数据源。
public abstract class BaseDao extends JdbcDaoSupport{
@Autowired
private void setDataSource(DriverManagerDataSource ds) {
super.setDataSource(ds);
}
}
(3)从Spring事务环境中获取数据库连接。
public Connection openConnection() throws Exception{
return this.getConnection();
}
(4)关闭数据库连接,如果当前为Spring事务环境则不关闭。
public void closeConnection(Connection con) {
boolean isTransaction = DataSourceUtils.isConnectionTransactional
(con, this.getDataSource());
if(isTransaction) {
Log.logger.info("事务环境,重用数据库连接");
}else {
Log.logger.info("非事务环境,关闭数据库连接");
try {
con.close();
} catch (SQLException e) {
Log.logger.error(e.getMessage(),e);
}
}
}
5.2.4 封装SpringFactory
在com.icss.util包下,自定义工厂类,封装Spring的IoC容器,确保IoC容器为单例模式,同时封装getBean()方法。
public class SpringFactory {
private static ApplicationContext ctx;
static {
ctx = new ClassPathXmlApplicationContext("beans.xml");
}
public static Object getBean(String name) throws BeansException{
return ctx.getBean(name);
}
public static T getBean(Class requiredType) throws BeansException{
return ctx.getBean(requiredType);
}
}
5.2.5 定义Spring Bean和依赖关系
使用@Service定义逻辑层的Bean,使用@Repository定义持久层的Bean,同时使用Autowire注入模式。
(1)定义逻辑层的Bean。
@Service
public class BookBiz implements IBook{}
@Service
public class UserBiz implements IUser{}
(2)定义持久层的Bean。
@Repository
public class BookDao extends BaseDao implements IBookDao{}
@Repository
public class UserDao extends BaseDao implements IUserDao{}
(3)Autowire注入Bean的依赖。
@Service
public class BookBiz implements IBook{
@Autowired
private IBookDao bookDao;
}
@Service
public class UserBiz implements IUser{
@Autowired
private IUserDao userDao;
}
5.2.6 配置声明性事务
在当当书城项目中,用户在商品结算页进行付款时,需要创建订单、创建订单明细、账户扣款、减少图书库存等很多步操作,这些操作具有ACID特性,因此应该使用事务管理。在当当书城基础版中使用本地事务进行付款管理,本节使用Spring的声明性事务管理。
下面使用@Transactional注解,对用户付款行为使用声明性事务进行管理。
@Transactional(rollbackFor=Throwable.class)
public void buyBooks(String uname, double allMoney,
MapshopCar) throws Exception {
if(uname== null || uname.equals("")) {
throw new Exception("用户名不能为空");
}
if(allMoney<=0) {
throw new Exception("金额错误");
}
if(shopCar==null || shopCar.size()==0) {
throw new Exception("购物车为空");
}
userDao.updateUserAccount(uname, -allMoney);
userDao.addBuyRecord(uname, allMoney, shopCar);
}
5.2.7 控制器调用Bean
在当当书城的Servlet中,调用业务逻辑对象时,不能再使用传统的new方式创建对象,而是应该从IoC容器中获取Bean对象。下面以当当书城主页的MainAction为例,其他项目代码参见本书配套资源。
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
IBook ibook = SpringFactory.getBean(IBook.class);
try {
List bks = ibook.getMainBook();
request.setAttribute("bks", bks);
request.getRequestDispatcher("/jsp/main.jsp").forward(req, res);
} catch (Exception e) {
Log.logger.error(e.getMessage(),e);
request.getRequestDispatcher("/error/err.jsp").forward(req, res);
} }
5.2.8 项目部署
参见本书配套资源中的DangSpring项目,这是一个Maven Web项目,项目部署到Tomcat 10.1中。注意:项目部署后,由于Tomcat 10.1中已存在Servlet和JSP环境,这会导致环境冲突报错。因此项目部署后,在启动Tomcat前,需要进入Tomcat 10.1的webapps目录,找到DangSpring的WEB-INF\lib目录,删除该目录下jakarta.servlet.jsp和jakarta.servlet-api等相关包。
134
135