在当今数字化零售时代,电子产品销售行业面临着传统模式效率低下、信息不对称、用户体验差等核心挑战。针对这些痛点,我们设计并实现了一套基于SSH集成框架的企业级电子产品B2C交易平台。该平台采用成熟稳定的技术架构,为电子消费品商家提供完整的线上销售解决方案,同时为消费者打造便捷高效的购物体验。
系统架构与技术栈深度解析
本平台采用经典的三层架构模式,各层技术选型经过精心考量。表现层使用Struts2框架,其强大的拦截器机制能够有效处理用户请求过滤、权限验证等横切关注点。通过OGNL表达式语言实现数据在Action与JSP页面间的灵活传递,确保业务逻辑与视图展示的清晰分离。
业务逻辑层由Spring框架的IoC容器统一管理,采用依赖注入方式组织各个Service组件。这种设计显著降低了模块间的耦合度,使得系统具备良好的可测试性和可维护性。Spring的声明式事务管理为订单处理、库存更新等关键业务操作提供了可靠的数据一致性保障。
数据持久层基于Hibernate ORM框架构建,通过对象关系映射将Java实体与数据库表结构建立关联。Hibernate的一级缓存和查询缓存机制有效提升了数据访问性能,而其丰富的映射策略支持复杂的关联关系处理。
前端技术栈选用JSP结合JSTL标签库进行动态内容渲染,辅以JavaScript实现丰富的用户交互效果,CSS负责界面样式美化。整个项目采用Maven进行依赖管理和构建流程标准化,确保开发环境的统一性。
数据库设计亮点与优化策略
数据库设计是系统稳定性的基石,本平台的表结构设计体现了多个技术优化点。
商品订单表(sp_order)的外键约束设计
CREATE TABLE `sp_order` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`num` int(11) DEFAULT NULL COMMENT '数量',
`sp_id` int(11) DEFAULT NULL COMMENT '商品ID',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`code` varchar(255) DEFAULT NULL COMMENT '订单号',
PRIMARY KEY (`id`),
KEY `FK_8hvg6wn4chkpvb4ou2stckwpf` (`sp_id`),
KEY `FK_rl0sqd6lc3rt9m9b72ogrcbjl` (`user_id`),
CONSTRAINT `FK_8hvg6wn4chkpvb4ou2stckwpf` FOREIGN KEY (`sp_id`) REFERENCES `sp` (`id`),
CONSTRAINT `FK_rl0sqd6lc3rt9m9b72ogrcbjl` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='商品订单表'
订单表的设计充分考虑了数据完整性和查询性能。通过外键约束确保订单记录的商品ID和用户ID必须在对应的商品表和用户表中存在,这种参照完整性约束有效防止了脏数据的产生。同时为sp_id和user_id字段建立索引,显著提升了多表关联查询的效率,特别是在订单列表页面需要同时展示商品信息和用户信息时效果尤为明显。
商品表(sp)的软删除设计
CREATE TABLE `sp` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`isDelelet` int(11) DEFAULT NULL COMMENT '是否删除',
`jj` varchar(255) DEFAULT NULL COMMENT '简介',
`price` varchar(255) DEFAULT NULL COMMENT '价格',
`type` int(11) DEFAULT NULL COMMENT '类型',
`urls` varchar(255) DEFAULT NULL COMMENT '图片地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='商品表'
商品表采用软删除机制,通过isDelelet字段标记记录是否被逻辑删除而非物理删除。这种设计具有重要价值:首先,保留了历史数据的完整性,便于后续的数据分析和审计追踪;其次,避免了因物理删除导致的关联数据不一致问题;最后,支持商品下架后的快速重新上架操作。价格字段选用varchar类型而非decimal,虽然牺牲了部分数值计算精度,但更好地适应了电子产品促销活动中常见的价格区间表示需求。
用户表(user)的扩展性设计
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`createTime` datetime DEFAULT NULL COMMENT '创建时间',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`phone` varchar(255) DEFAULT NULL COMMENT '电话',
`realname` varchar(255) DEFAULT NULL COMMENT '真实名字',
`username` varchar(255) DEFAULT NULL COMMENT '用户名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='用户表'
用户表设计考虑了系统的可扩展性。createTime字段记录用户注册时间,为后续的用户行为分析提供数据基础。realname与username字段分离设计,既支持用户使用昵称登录,又保留了真实姓名信息用于订单配送等场景。电话字段为后续的短信验证、移动端登录等功能预留了扩展空间。
核心功能实现深度解析
用户认证与权限管理
系统采用基于角色的访问控制模型,不同角色拥有不同的操作权限。用户登录功能通过Struts2的拦截器实现安全控制:
public class UserLoginAction extends ActionSupport {
private String username;
private String password;
private UserService userService;
public String execute() {
try {
User user = userService.validateLogin(username, password);
if (user != null) {
ActionContext.getContext().getSession().put("currentUser", user);
return SUCCESS;
} else {
addActionError("用户名或密码错误");
return INPUT;
}
} catch (Exception e) {
addActionError("登录过程出现异常");
return ERROR;
}
}
// Getter和Setter方法
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 void setUserService(UserService userService) {
this.userService = userService;
}
}

登录拦截器确保未认证用户无法访问受保护的资源:
public class AuthInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Map<String, Object> session = invocation.getInvocationContext().getSession();
Object user = session.get("currentUser");
if (user == null) {
return "login";
}
return invocation.invoke();
}
}
商品管理模块
商品管理模块支持商品的CRUD操作,采用Hibernate实现数据持久化:
@Service
@Transactional
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public void saveProduct(Sp product) {
productDao.save(product);
}
@Override
@Transactional(readOnly = true)
public List<Sp> findAllProducts() {
return productDao.findAll();
}
@Override
@Transactional(readOnly = true)
public Sp findProductById(Integer id) {
return productDao.findById(id);
}
@Override
public void updateProduct(Sp product) {
productDao.update(product);
}
@Override
public void deleteProduct(Integer id) {
Sp product = productDao.findById(id);
if (product != null) {
product.setIsDelelet(1); // 软删除
productDao.update(product);
}
}
}

商品实体类映射配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.electronicsmall.entity.Sp" table="sp">
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="isDelelet" column="isDelelet"/>
<property name="jj" column="jj"/>
<property name="price" column="price"/>
<property name="type" column="type"/>
<property name="urls" column="urls"/>
</class>
</hibernate-mapping>
购物车与订单处理
购物车功能采用Session存储临时数据,订单处理确保事务一致性:
@Service
@Transactional
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private ProductDao productDao;
@Override
@Transactional(rollbackFor = Exception.class)
public String createOrder(SpOrder order, List<CartItem> cartItems) {
try {
// 生成订单号
String orderCode = generateOrderCode();
order.setCode(orderCode);
// 保存订单主信息
orderDao.save(order);
// 处理订单明细和库存扣减
for (CartItem item : cartItems) {
Sp product = productDao.findById(item.getProductId());
if (product != null) {
// 检查库存
if (item.getQuantity() > getAvailableStock(product)) {
throw new InsufficientStockException("商品库存不足");
}
// 扣减库存
reduceStock(product, item.getQuantity());
// 保存订单明细
OrderDetail detail = new OrderDetail();
detail.setOrderId(order.getId());
detail.setProductId(item.getProductId());
detail.setQuantity(item.getQuantity());
detail.setPrice(item.getPrice());
orderDao.saveDetail(detail);
}
}
return orderCode;
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new RuntimeException("订单创建失败", e);
}
}
private String generateOrderCode() {
return "ORD" + System.currentTimeMillis() +
String.format("%04d", new Random().nextInt(10000));
}
}

商品搜索与分页展示
商品列表页面实现高效的分页查询和条件搜索:
@Repository
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
public Page<Sp> findProductsByCondition(ProductQuery query, int pageNum, int pageSize) {
StringBuilder hql = new StringBuilder("from Sp where isDelelet = 0");
Map<String, Object> params = new HashMap<>();
if (StringUtils.isNotBlank(query.getKeyword())) {
hql.append(" and (jj like :keyword or urls like :keyword)");
params.put("keyword", "%" + query.getKeyword() + "%");
}
if (query.getType() != null) {
hql.append(" and type = :type");
params.put("type", query.getType());
}
if (query.getMinPrice() != null) {
hql.append(" and cast(price as decimal) >= :minPrice");
params.put("minPrice", new BigDecimal(query.getMinPrice()));
}
if (query.getMaxPrice() != null) {
hql.append(" and cast(price as decimal) <= :maxPrice");
params.put("maxPrice", new BigDecimal(query.getMaxPrice()));
}
// 获取总记录数
String countHql = "select count(*) " + hql.toString();
Long totalCount = (Long) getHibernateTemplate()
.findByNamedParam(countHql, params.toArray(new String[0]),
params.values().toArray()).get(0);
// 获取分页数据
List<Sp> list = getHibernateTemplate()
.findByNamedParam(hql.toString() + " order by id desc",
params.toArray(new String[0]),
params.values().toArray(),
(pageNum - 1) * pageSize, pageSize);
return new Page<>(pageNum, pageSize, totalCount.intValue(), list);
}
}

实体模型设计与业务逻辑
订单实体与关联映射
订单实体类设计体现了业务对象的完整属性:
@Entity
@Table(name = "sp_order")
public class SpOrder implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "num")
private Integer quantity;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sp_id")
private Sp product;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@Column(name = "code")
private String orderCode;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time")
private Date createTime;
// 构造函数、getter和setter方法
public SpOrder() {
this.createTime = new Date();
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Integer getQuantity() { return quantity; }
public void setQuantity(Integer quantity) { this.quantity = quantity; }
public Sp getProduct() { return product; }
public void setProduct(Sp product) { this.product = product; }
public User getUser() { return user; }
public void setUser(User user) { this.user = user; }
public String getOrderCode() { return orderCode; }
public void setOrderCode(String orderCode) { this.orderCode = orderCode; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
}
Spring配置与依赖注入
应用上下文配置采用注解与XML混合方式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 组件扫描 -->
<context:component-scan base-package="com.electronicsmall.service"/>
<context:component-scan base-package="com.electronicsmall.dao"/>
<!-- 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/electronics_mall"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="maxPoolSize" value="50"/>
<property name="minPoolSize" value="5"/>
<property name="initialPoolSize" value="10"/>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>com/electronicsmall/entity/Sp.hbm.xml</value>
<value>com/electronicsmall/entity/User.hbm.xml</value>
<value>com/electronicsmall/entity/SpOrder.hbm.xml</value>
</list>
</property>
</bean>
<!-- 事务管理 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
功能展望与系统优化方向
基于当前系统架构,未来可以从以下几个方向进行深度优化和功能扩展:
1. 引入Redis缓存提升系统性能
在高并发场景下,商品信息、用户会话等热点数据的频繁访问会给数据库带来巨大压力。引入Redis作为二级缓存可以显著提升系统响应速度。
@Service
public class CachedProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String PRODUCT_KEY_PREFIX = "product:";