接着SpringMVC + Spring + MyBatis 整合(二)
SSM框架应用实例(图书管理系统)
一开始想就这样结束教程,但是发现其实很多人都还不会把这个SSM框架用起来,特别是mybatis部分。那我现在就以最常见的“图书管理系统”中【查询图书】和【预约图书】业务来做一个demo吧!
首先新建数据库名为ssm
,再创建两张表:图书表book
和预约图书表appointment
,并且为book
表初始化一些数据,sql如下。
ssm.sql
-- 创建图书表 CREATE TABLE book( book_id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID', name VARCHAR(100) NOT NULL COMMENT '图书名称', number int(11) NOT NULL COMMENT '馆藏数量', PRIMARY KEY(book_id) )ENGINE=INNODB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT '图书表'; -- 初始化图书数据 INSERT INTO book (book_id, name, number) VALUES (1000, 'Java程序设计', 10), (1001, '数据结构', 10), (1002, '设计模式', 10), (1003, '编译原理', 10); -- 创建预约图书表 CREATE TABLE appointment( book_id BIGINT(20) NOT NULL COMMENT '图书ID', student_id BIGINT(20) NOT NULL COMMENT '学号', appoint_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '预约时间', PRIMARY KEY(book_id, student_id), INDEX idx_appoint_time(appoint_time) )ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT '预约图书表'
在entity
包中添加两个对应的实体,图书实体Book.java
和预约图书实体Appointment.java
。
Book.java
package com.ray.entity; /** * @author Ray * @date 2018/5/2 0002 * 图书类 */ public class Book { private long bookId; // 图书ID private String name; // 图书名称 private int number; // 馆藏数量 // 构造方法,getter和setter方法,toString方法 public Book() { } public Book(long bookId, String name, int number) { this.bookId = bookId; this.name = name; this.number = number; } public long getBookId() { return bookId; } public void setBookId(long bookId) { this.bookId = bookId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public String toString() { return "Book{" + "bookId=" + bookId + ", name='" + name + '\'' + ", number=" + number + '}'; } }
Appointment.java
package com.ray.entity; import java.util.Date; /** * @author Ray * @date 2018/5/2 0002\ * 预约图书实体 */ public class Appointment { private long bookId; // 图书ID private long studentId; // 学号 private Date appointTime; // 预约时间 // 多对一的复合属性 private Book book; // 图书实体 // 构造方法,getter和setter方法,toString方法 public Appointment() { } public Appointment(long bookId, long studentId, Date appointTime, Book book) { this.bookId = bookId; this.studentId = studentId; this.appointTime = appointTime; this.book = book; } public long getBookId() { return bookId; } public void setBookId(long bookId) { this.bookId = bookId; } public long getStudentId() { return studentId; } public void setStudentId(long studentId) { this.studentId = studentId; } public Date getAppointTime() { return appointTime; } public void setAppointTime(Date appointTime) { this.appointTime = appointTime; } public Book getBook() { return book; } public void setBook(Book book) { this.book = book; } @Override public String toString() { return "Appointment{" + "bookId=" + bookId + ", studentId=" + studentId + ", appointTime=" + appointTime + ", book=" + book + '}'; } }
在dao包新建 接口 BookDao.java 和 Appointment.java
BookDao.java
package com.ray.dao; import com.ray.entity.Book; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @author Ray * @date 2018/5/2 0002 */ public interface BookDao { /** * 通过ID查询单本图书 * @param id * @return */ Book queryById(long id); /** * 查询所有图书 * @param offset 查询起始位置 * @param limit 查询条数 * @return */ List<Book> queryAll(@Param("offset") int offset, @Param("limit") int limit); /** * 减少馆藏数量 * @param bookId * @return 如果影响行数等于>1,表示更新的记录行数 */ int reduceNumber(long bookId); }
AppointmentDao.java
package com.ray.dao; import com.ray.entity.Appointment; import org.apache.ibatis.annotations.Param; /** * @author Ray * @date 2018/5/2 0002 */ public interface AppointmentDao { /** * 插入预约图书记录 * @param bookId * @param studentId * @return 插入的行数 */ int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId); /** * 通过主键查询预约图书记录,并且携带图书实体 * @param bookId * @param studentId * @return */ Appointment queryByKeyWithBook(@Param("bookId") long bookId, @Param("studentId") long studentId); }提示 :这里为什么要给方法的参数添加
@Param
注解呢?是因为该方法有两个或以上的参数,一定要加,不然mybatis识别不了。上面的
BookDao
接口的
queryById
方法和
reduceNumber
方法只有一个参数
book_id
,所以可以不用加
@Param
注解,当然加了也无所谓~注意,这里不需要实现dao接口不用编写daoImpl, mybatis会给我们动态实现,但是我们需要编写相应的mapper。 在mapper
目录里新建两个文件BookDao.xml
和AppointmentDao.xml
,分别对应上面两个dao接口,代码如下。
BookDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ray.dao.BookDao"> <!-- 目的:为dao接口方法提供sql语句配置 --> <select id="queryById" resultType="Book" parameterType="long"> <!-- 具体的sql语句 --> SELECT book_id, name, number FROM book WHERE book_id = #{id} </select> <select id="queryAll" resultType="Book"> SELECT book_id, name, number FROM book ORDER BY book_id LIMIT #{offset}, #{limit} </select> <update id="reduceNumber"> UPDATE book SET number = number - 1 WHERE book_id = #{bookId} AND number > 0 </update> </mapper>
AppointmentDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ray.dao.AppointmentDao"> <insert id="insertAppointment"> <!-- ignore 主键冲突,报错 --> INSERT ignore INTO appointment(book_id, student_id) VALUES (#{bookId}, #{studentId}) </insert> <select id="queryByKeyWithBook" resultType="Appointment"> <!-- 告诉MyBatis如何把结果映射到Appointment同时映射book属性 --> <!-- 可以自由控制SQL --> SELECT a.book_id, a.student_id, a.appoint_time, b.book_id "book.book_id", b.name "book.name", b.number "book.number" FROM appointment a INNER JOIN book b ON a.book_id = b.book_id WHERE a.book_id = #{bookId} AND a.student_id = #{studentId} </select> </mapper>mapper总结 :
namespace
是该xml对应的接口全名,
select
和
update
中的
id
对应方法名,
resultType
是返回值类型,
parameterType
是参数类型(这个其实可选),最后
#{...}
中填写的是方法的参数,看懂了是不是很简单!!我也这么觉得~ 还有一个小技巧要交给大家,就是在返回
Appointment
对象包含了一个属性名为
book
的Book对象,那么可以使用
"book.属性名"
的方式来取值,看上面
queryByKeyWithBook
方法的sql。dao
层写完了,接下来test
对应的package
写我们测试方法吧。 因为我们之后会写很多测试方法,在测试前需要让程序读入spring-dao和mybatis等配置文件,所以我这里就抽离出来一个BaseTest
类,只要是测试方法就继承它,这样那些繁琐的重复的代码就不用写那么多了~
BaseTest.java
package com.ray; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Ray * @date 2018/5/2 0002 * 配置spring和junit整合,junit启动时加载springIOC容器 spring-test,junit */ @RunWith(SpringJUnit4ClassRunner.class) // 告诉junit spring配置文件 @ContextConfiguration({"classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml"}) public class BaseTest { }
因为spring-service
在service
层的测试中会时候到,这里也一起引入算了!
新建BookDaoTest.java
和AppointmentDaoTest.java
两个dao测试文件。
BookDaoTest.java
package com.ray; import com.ray.dao.BookDao; import com.ray.entity.Book; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; /** * @author Ray * @date 2018/5/2 0002 */ public class BookDaoTest extends BaseTest { @Autowired private BookDao bookDao; @Test public void testQueryById(){ long bookId = 1000; Book book = bookDao.queryById(bookId); System.out.println(book); } @Test public void testQueryAll(){ List<Book> books = bookDao.queryAll(0,4); for (Book book : books) { System.out.println(book); } } @Test public void testReduceNumber(){ long bookId = 1000; int update = bookDao.reduceNumber(bookId); System.out.println("update= " + update); Book book = bookDao.queryById(bookId); System.out.println(book); } }
BookDaoTest测试结果
testQueryById
testQueryAll
testReduceNumber
AppointmentDaoTest.java
package com.ray; import com.ray.dao.AppointmentDao; import com.ray.entity.Appointment; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; /** * @author Ray * @date 2018/5/2 0002 */ public class AppointmentDaoTest extends BaseTest { @Autowired private AppointmentDao appointmentDao; @Test public void testInsertAppointment(){ long bookId = 1000; long studentId = 1234567890L; int insert = appointmentDao.insertAppointment(bookId, studentId); System.out.println("insert= " + insert); } @Test public void testQueryByKeyWithBook(){ long bookId = 1000; long studentId = 1234567890L; Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId); System.out.println(appointment); System.out.println(appointment.getBook()); } }
AppointmentDaoTest测试结果
testInsertAppointment
testQueryByKeyWithBook
到此,我们的SSM框架整合配置,与应用实例部分已经结束了,我把所有源码和jar包一起打包放在了我的GitHub上,需要的可以去下载