乐者为王

Do one thing, and do it well.

实现图书的增删查改(CRUD)

图书的MySQL数据库创建脚本:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE books (
    id int NOT NULL AUTO_INCREMENT,
    title varchar(255) NOT NULL,
    description text,
    image_url varchar(255),
    price decimal(8, 2) NOT NULL,
    author varchar(255) NOT NULL,
    isbn varchar(255) NOT NULL,
    publisher varchar(255) NOT NULL,
    user_id int NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf-8;

图书实体类文件Book.java主要代码:

1
2
3
4
5
6
7
8
9
10
public class Book {
    private Long id;
    private String title;
    private String description;
    private String image_url;
    private double price;
    private String author;
    private String isbn;
    private String publisher;
    private User user;

在User类中添加集合变量books:

1
2
public class User {
    private Set<Book> books = new HashSet<Book>();

在User.hbm.xml中配置和图书的一对多关联:

1
2
3
4
5
<!-- Bidirectional one-to-many association to Book -->
<set name="books" inverse="true">
    <key column="user_id" not-null="true" />
    <one-to-many class="com.codemany.book.model.Book" />
</set>

图书的表映射文件Book.hbm.xml代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<hibernate-mapping>
    <class name="com.codemany.book.model.Book" table="books">
        <id name="id">
            <generator class="increment" />
        </id>

        <property name="title" not-null="true" />
        <property name="description" />
        <property name="image_url" />
        <property name="price" not-null="true" />
        <property name="author" not-null="true" />
        <property name="isbn" not-null="true" />
        <property name="publisher" not-null="true" />

        <many-to-one name="user" column="user_id" not-null="true" />
    </class>
</hibernate-mapping>

BookService.java中的代码相对简单,因为没什么复杂的业务逻辑,只是负责把Action和Dao这两层连接起来。

BookDao.java中的代码主要实现图书的读取,更新和删除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.codemany.book.dao;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.codemany.book.model.Book;

public class BookDao {
    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public List<Book> getBookList() {
        Session session = sessionFactory.openSession();
        try {
            Query query = session.createQuery("from Book b");
            List<Book> bookList = query.list();
            return bookList;
        } finally {
            session.close();
        }
    }

    public Book getBook(Long bookId) {
        Session session = sessionFactory.openSession();
        try {
            Query query = session.createQuery("from Book b where b.id = :id");
            query.setLong("id", bookId);
            query.setMaxResults(1);
            return (Book)query.uniqueResult();
        } finally {
            session.close();
        }
    }

    public void saveOrUpdateBook(Book book) {
        Session session = sessionFactory.openSession();
        Transaction ts = null;
        try {
            ts = session.beginTransaction();
            session.saveOrUpdate(book);
            ts.commit();
        } finally {
            session.close();
        }
    }

    public void deleteBook(Long bookId) {
        Session session = sessionFactory.openSession();
        Transaction ts = null;
        try {
            ts = session.beginTransaction();
            Book book = (Book)session.get(Book.class, bookId);
            session.delete(book);
            ts.commit();
        } finally {
            session.close();
        }
    }
}

BookAction.java负责协调图书应用程序间的运转。接收访问请求,与模型进行交互,将合适的视图展示给用户:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.codemany.book.action;

import java.util.List;

import com.codemany.account.model.User;
import com.codemany.book.model.Book;
import com.codemany.book.service.BookService;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class BookAction extends ActionSupport {
    private static final long serialVersionUID = 2538923417705852774L;

    private Long bookId;
    private Book book;
    private List<Book> bookList;
    private BookService bookService;

    public String list() throws Exception {
        bookList = bookService.getBookList();
        return "list";
    }

    public String show() throws Exception {
        book = bookService.getBook(bookId);
        return "show";
    }

    public String input() throws Exception {
        if (bookId != null) {
            book = bookService.getBook(bookId);
        }
        return INPUT;
    }

    public String saveOrUpdate() throws Exception {
        User user = (User)ActionContext.getContext().getSession().get("user");
        book.setUser(user);
        bookService.saveOrUpdateBook(book);
        return SUCCESS;
    }

    public String delete() throws Exception {
        bookService.deleteBook(bookId);
        return SUCCESS;
    }

在applicationContext.xml中添加Book的相关配置,如下面代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="mappingResources">
        <list>
            <value>com/codemany/book/model/Book.hbm.xml</value>
        </list>
    </property>
</bean>

<!-- book -->
<bean id="bookDao" class="com.codemany.book.dao.BookDao">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="bookService" class="com.codemany.book.service.BookService">
    <property name="bookDao" ref="bookDao" />
</bean>

<bean id="bookAction" class="com.codemany.book.action.BookAction" scope="prototype">
    <property name="bookService" ref="bookService" />
</bean>

在Struts 2配置文件struts.xml中include文件book.xml。以下是book.xml文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <package name="book" extends="struts-default">
        <action name="*Book" method="{1}" class="bookAction">
            <result name="list">/books/list.jsp</result>
            <result name="show">/books/show.jsp</result>
            <result name="input">/books/form.jsp</result>
            <result type="redirectAction">listBook</result>
        </action>
    </package>
</struts>

视图文件show.jsp作为显示单本图书的信息,代码很简单,就不放出来了,只把list.jsp和form.jsp的代码列出来。

list.jsp用来显示所有的图书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<body>
    <h1>Listing books</h1>

    <table>
        <tr>
            <th>Title</th>
            <th>Description</th>
            <th>Image url</th>
            <th>Price</th>
            <th>Author</th>
            <th>ISBN</th>
            <th>Publisher</th>
            <th>Action</th>
        </tr>

        <s:iterator value="bookList">
        <tr>
            <td><s:property value="title" /></td>
            <td><s:property value="description" /></td>
            <td><s:property value="image_url" /></td>
            <td><s:property value="price" /></td>
            <td><s:property value="author" /></td>
            <td><s:property value="isbn" /></td>
            <td><s:property value="publisher" /></td>
            <td>
                <s:a href="showBook.action?bookId=%{id}">Show</s:a>
                <s:a href="inputBook.action?bookId=%{id}">Edit</s:a>
                <s:a href="deleteBook.action?bookId=%{id}" method="delete">Destroy</s:a>
            </td>
        </tr>
        </s:iterator>
    </table>

    <s:a action="inputBook">Add</s:a>
</body>

当用户新建或者更新图书时,视图form.jsp就上场了。因为被两个逻辑使用,显示的标题等信息也不相同,所以需要判断图书的id是否存在,如果不存在的话即是新建业务,否则就是更新操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<body>
    <s:if test="book.id != null">
        <h1>Editing book</h1>
    </s:if>
    <s:else>
        <h1>Add book</h1>
    </s:else>

    <s:form action="saveOrUpdateBook" method="post">
        <div class="field">
            <s:hidden name="book.id" value="%{book.id}" />
        </div>
        <div class="field">
            <s:textfield name="book.title" label="Title" />
        </div>
        <div class="field">
            <s:textarea name="book.description" label="Description" rows="5" cols="25" />
        </div>
        <div class="field">
            <s:textfield name="book.image_url" label="Image URL" />
        </div>
        <div class="field">
            <s:textfield name="book.price" label="Price" />
        </div>
        <div class="field">
            <s:textfield name="book.author" label="Author" />
        </div>
        <div class="field">
            <s:textfield name="book.isbn" label="ISBN" />
        </div>
        <div class="field">
            <s:textfield name="book.publisher" label="Publisher" />
        </div>
        <div class="actions">
            <s:if test="book.id != null">
            <s:submit value="Update" />
            </s:if>
            <s:else>
            <s:submit value="Save" />
            </s:else>
      </div>
    </s:form>

    <s:if test="book.id != null">
        <s:a href="showBook.action?bookId=%{book.id}">Show</s:a> |
    </s:if>
    <s:a action="listBook">Back</s:a>
</body>

最后还要完成图书模型的服务端校验。Struts 2的校验文件有两种格式:ActionName-validation.xml和ActionName-alias-validation.xml。第一种会对该Action中的每个方法进行校验,不符合只对saveOrUpdate校验的要求。在BookAction.java同目录下创建BookAction-saveOrUpdateBook-validation.xml文件:

1
2
3
4
5
6
7
8
<validators>
    <field name="book">
        <field-validator type="visitor">
            <param name="appendPrefix">true</param>
            <message />
        </field-validator>
    </field>
</validators>

当然,还要在Book.java所在的位置创建Book-validation.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<validators>
    <field name="title">
        <field-validator type="required">
            <message>Price is required</message>
        </field-validator>
    </field>

    <field name="price">
        <field-validator type="required">
            <message>Price is required</message>
        </field-validator>
        <field-validator type="double">
            <param name="minInclusive">0.01</param>
            <message>Price should be at least 0.01</message>
        </field-validator>
    </field>

看着BookAction-saveOrUpdateBook-validation.xml这么长的文件名是不是有点无语,没关系,BookAction-validation.xml配置文件还是可以用的,只要在BookAction.java中那些不需要进行校验的方法上添加@SkipValidation;也可以在action配置中启用validation.excludeMethods参数:

1
2
3
4
<action name="*Book" method="{1}" class="bookAction">
    <interceptor-ref name="defaultStack">
        <param name="validation.excludeMethods">list,show,input,delete</param>
    </interceptor-ref>

代码下载:https://github.com/dohkoos/JBookShelf

Comments