第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