技術棧:Spring 4.3 + MyBatis 3.4 + Maven + MySQL 5.5 + Tomcat 7/8 + C3P0 連接池 + Log4j2 + JUnit 4
實現功能:圖書增刪改查、事務管理、單元測試、簡易 MVC 架構
src/main/webappsrc/main/java、src/main/resources、src/main/webappCREATE TABLE `books` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號', `title` varchar(100) NOT NULL COMMENT '書名', `price` decimal(10,2) DEFAULT NULL COMMENT '價格', `publishDate` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '出版日期', PRIMARY KEY (`id`), UNIQUE KEY `title` (`title`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 初始數據 INSERT INTO `books` VALUES (1, 'Java編程思想', 98.50, '2005-01-02'), (2, 'HeadFirst 設計模式', 55.70, '2010-11-09'), (3, '第一行代碼Android', 69.90, '2015-06-23'), (4, 'C++編程思想', 88.50, '2004-01-09'), (5, 'HeadFirst Java', 55.70, '2013-12-17'), (6, '瘋狂Android', 19.50, '2014-07-31');
<properties>
<spring.version>4.3.0.RELEASE</spring.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring 核心 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency>
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.9</version></dependency>
<!-- MyBatis 整合 -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.1</version></dependency>
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency>
<!-- 數據庫連接池 -->
<dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version></dependency>
<!-- 其他 -->
<dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.6.1</version></dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version></dependency>
</dependencies>
package com.zhangguo.bookstore.entities;
import java.util.Date;
public class Book {
private int id;
private String title;
private double price;
private Date publishDate;
// 構造器、Getter/Setter 省略
}
package com.zhangguo.bookstore.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.zhangguo.bookstore.entities.Book;
public interface BookDAO {
List<Book> getAllBooks();
Book getBookById(@Param("id") int id);
int add(Book entity);
int delete(int id);
int update(Book entity);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhangguo.bookstore.mapper.BookDAO">
<select id="getAllBooks" resultType="Book">
select id, title, price, publishDate from books
</select>
<select id="getBookById" resultType="Book">
select id, title, price, publishDate from books where id=#{id}
</select>
<insert id="add">
insert into books(title, price, publishDate) values(#{title}, #{price}, #{publishDate})
</insert>
<delete id="delete">
delete from books where id=#{id}
</delete>
<update id="update">
update books set title=#{title}, price=#{price}, publishDate=#{publishDate} where id=#{id}
</update>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<!-- 1. 屬性佔位符 -->
<context:property-placeholder location="classpath*:db.properties" />
<!-- 2. C3P0 數據源 -->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.uid}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="acquireIncrement" value="5"/>
<property name="initialPoolSize" value="10"/>
<property name="minPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
</bean>
<!-- 3. SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<property name="typeAliasesPackage" value="com.zhangguo.bookstore.entities"/>
<property name="mapperLocations" value="classpath*:com/zhangguo/bookstore/mapper/*Mapper.xml"/>
</bean>
<!-- 4. Mapper 掃描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.zhangguo.bookstore.mapper"/>
</bean>
<!-- 5. 聲明式事務 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 6. 組件掃描 -->
<context:component-scan base-package="com.zhangguo.bookstore"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
@Service
public class BookService {
@Resource BookDAO bookdao;
public List<Book> getAllBooks() { return bookdao.getAllBooks(); }
public Book getBookById(int id) { return bookdao.getBookById(id); }
public int add(Book entity) throws Exception {
if (entity.getTitle() == null || entity.getTitle().isEmpty())
throw new Exception("書名不能為空");
return bookdao.add(entity);
}
@Transactional
public int add(Book entity1, Book entityBak) { // 事務測試用
int rows = bookdao.add(entity1);
rows += bookdao.add(entityBak);
return rows;
}
public int delete(int id) { return bookdao.delete(id); }
public int delete(String[] ids) { /* 批量刪除實現 */ }
public int update(Book entity) { return bookdao.update(entity); }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestBookService {
@Autowired BookService bookService;
@Test public void testGetAllBooks() { assertNotNull(bookService.getAllBooks()); }
@Test public void testAdd() { /* 測試添加 */ }
@Test public void testDeleteInt() { assertEquals(1, bookService.delete(9)); }
@Test public void testDeleteStringArray() { /* 批量刪除 */ }
@Test public void testUpdate() { /* 更新測試 */ }
@Test public void testGetBookById() { assertNotNull(bookService.getBookById(1)); }
@Test public void testAddDouble() { /* 事務回滾測試:同名書添加兩次應失敗 */ }
}
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
@Component
public class CtxUtil implements ApplicationContextAware {
public static ApplicationContext ctx;
@Override public void setApplicationContext(ApplicationContext ctx) { CtxUtil.ctx = ctx; }
public static <T> T getBean(Class<T> clazz) { return ctx.getBean(clazz); }
public static Object getBean(String name) { return ctx.getBean(name); }
}
public class BaseController extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) {
req.setCharacterEncoding("UTF-8");
String act = req.getParameter("act");
if (act == null || act.isEmpty()) act = "execute";
try {
Method method = this.getClass().getMethod(act, HttpServletRequest.class, HttpServletResponse.class);
String target = (String) method.invoke(this, req, resp);
if (target.startsWith("redirect:")) resp.sendRedirect(target.substring(9));
else req.getRequestDispatcher(target).forward(req, resp);
} catch (Exception e) { resp.sendError(400, e.getMessage()); }
}
public String execute(HttpServletRequest req, HttpServletResponse resp) { return "error.jsp"; }
}
@WebServlet("/BookController.do")
public class BookController extends BaseController {
BookService bookService;
@Override public void init() { bookService = CtxUtil.getBean(BookService.class); }
public String ListBook(HttpServletRequest req, HttpServletResponse resp) {
req.setAttribute("books", bookService.getAllBooks());
return "ListBook.jsp";
}
public String Delete(HttpServletRequest req, HttpServletResponse resp) {
int id = Integer.parseInt(req.getParameter("id"));
req.setAttribute("message", bookService.delete(id) > 0 ? "刪除成功" : "刪除失敗");
req.setAttribute("books", bookService.getAllBooks());
return "ListBook.jsp";
}
}
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<table border="1" width="100%">
<tr><th>編號</th><th>書名</th><th>價格</th><th>出版日期</th><th>操作</th></tr>
<c:forEach var="book" items="${books}">
<tr>
<td>${book.id}</td><td>${book.title}</td><td>${book.price}</td><td>${book.publishDate}</td>
<td><a href="BookController.do?act=Delete&id=${book.id}">刪除</a></td>
</tr>
</c:forEach>
</table>
<p>${message}</p>
<jsp:forward page="BookController.do?act=ListBook"/>