[#3840] Cleanup and documentation

This commit is contained in:
lukaseder 2014-12-03 15:44:20 +01:00
parent ba99612ba0
commit bd3ca03e6b
9 changed files with 169 additions and 138 deletions

View File

@ -12,13 +12,6 @@
</attributes>
</classpathentry>
<classpathentry kind="src" path="src/main/webapp"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry kind="src" path="target/generated-sources/jooq-h2"/>
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">

View File

@ -2,5 +2,4 @@ eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/main/resources=UTF-8
encoding//src/main/webapp/library.xhtml=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8

View File

@ -14,3 +14,14 @@ $ cd jOOQ-examples/jOOQ-javaee-example
...
$ mvn clean install
```
After the above, you should find a `jooq-javaee-example.war` file in
```
$ pwd
/path/to/checkout/dir
$ cd jOOQ-examples/jOOQ-javaee-example/target
...
```
You can deploy this war file in your WildFly AS or any other application server. The example will use an embedded H2 database, which should be pre-filled with the library example H2 database. It uses a non-managed `DataSource`, which is configured and consumed directly by the application itself.

View File

@ -72,11 +72,6 @@
<artifactId>jooq</artifactId>
<version>${org.jooq.version}</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>

View File

@ -62,36 +62,74 @@ import org.jooq.SortField;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.UpdatableRecord;
import org.jooq.example.db.h2.tables.Author;
import org.jooq.example.db.h2.tables.records.AuthorRecord;
import org.jooq.example.db.h2.tables.records.BookRecord;
import org.jooq.example.javaee.ejb.AuthorsEJB;
import org.jooq.example.javaee.ejb.LibraryEJB;
import org.jooq.impl.DSL;
/**
* A bean to be used from JSF pages.
* <p>
* The bean is session scoped such that we can have session-based caches of
* database content such as authors or books in this bean. In this simple
* example, we haven't gone into concurrency situations where multiple sessions
* update the library database at the same time, in case of which we might need
* to turn on optimistic locking in jOOQ.
*
* @author Lukas Eder
*/
@Named("authors")
@Named("library")
@SessionScoped
public class Authors implements Serializable {
public class Library implements Serializable {
private static final long serialVersionUID = 1L;
@EJB
private AuthorsEJB ejb;
private LibraryEJB ejb;
// Caches from the DB
// Various caches from the DB
// -------------------------------------------------------------------------
/**
* An empty {@link AuthorRecord} that can be used to insert new authors.
*/
private AuthorRecord newAuthor;
/**
* An empty {@link BookRecord} that can be used to insert new books.
*/
private BookRecord newBook;
/**
* The reference to the {@link Record} that is currently being edited.
*/
private Record edit;
/**
* The sort field for each {@link Table}.
*/
private Map<Table<?>, SortField<?>> sort = Stream.of(AUTHOR.ID, BOOK.ID).collect(
toMap(f -> f.getTable(), f -> f.asc()));
/**
* A cache of all {@link AuthorRecord}s currently in the database.
*/
private Result<AuthorRecord> authors;
/**
* A copy of {@link #authors} that is always sorted alphanumerically.
*/
private Result<AuthorRecord> authorsAlphanumeric;
/**
* A cache of all {@link BookRecord}s currently in the database.
*/
private Result<BookRecord> books;
// Data access methods
// -------------------------------------------------------------------------
public Result<AuthorRecord> getAuthors() {
if (authors == null)
authors = ejb.fetchAuthors(sort.get(AUTHOR));
@ -113,10 +151,6 @@ public class Authors implements Serializable {
return getAuthors().intoMap(AUTHOR.ID);
}
public Map<String, Field<?>> getAuthorColumns() {
return getColumns(AUTHOR);
}
public Result<BookRecord> getBooks() {
if (books == null)
books = ejb.fetchBooks(sort.get(BOOK));
@ -124,36 +158,70 @@ public class Authors implements Serializable {
return books;
}
public Map<String, Field<?>> getBookColumns() {
return getColumns(BOOK);
}
private Map<String, Field<?>> getColumns(Table<?> t) {
return Stream.of(t.fields()).collect(toMap(f -> f.getName(), f -> f));
}
public Record getEdit() {
return edit;
}
public Map<String, SortField<?>> getSort() {
return sort.entrySet().stream().collect(toMap(e -> e.getKey().getName(), e -> e.getValue()));
}
public AuthorRecord getNewAuthor() {
if (newAuthor == null)
newAuthor = ejb.newAuthor();
newAuthor = DSL.using(H2).newRecord(AUTHOR);
return newAuthor;
}
public BookRecord getNewBook() {
if (newBook == null)
newBook = ejb.newBook();
newBook = DSL.using(H2).newRecord(BOOK);
return newBook;
}
// UI state methods
// -------------------------------------------------------------------------
/**
* The record being edited.
*/
public Record getEdit() {
return edit;
}
/**
* A map containing <code>column name -> column</code> pairs of the
* {@link Author} table.
*/
public Map<String, Field<?>> getAuthorColumns() {
return getColumns(AUTHOR);
}
/**
* A map containing <code>column name -> column</code> pairs of the
* {@link Author} table.
*/
public Map<String, Field<?>> getBookColumns() {
return getColumns(BOOK);
}
/**
* Get a map containing <code>table name -> sort field</code> pairs.
*/
public Map<String, SortField<?>> getSort() {
return sort.entrySet().stream().collect(toMap(e -> e.getKey().getName(), e -> e.getValue()));
}
private Map<String, Field<?>> getColumns(Table<?> t) {
return Stream.of(t.fields()).collect(toMap(f -> f.getName(), f -> f));
}
private void reset() {
authors = null;
authorsAlphanumeric = null;
books = null;
edit = null;
}
// Actions
// -------------------------------------------------------------------------
/**
* Sort a table by a new field.
*/
public void sort(TableField<?, ?> field) {
SortField<?> previous = sort.get(field.getTable());
sort.put(field.getTable(),
@ -167,26 +235,26 @@ public class Authors implements Serializable {
reset();
}
/**
* Mark a record as the one being currently edited.
*/
public void edit(UpdatableRecord<?> record) {
edit = record;
}
/**
* Save a record back to the database.
*/
public void save(UpdatableRecord<?> author) {
ejb.store(author);
reset();
}
/**
* Delete a record from the database.
*/
public void delete(UpdatableRecord<?> author) {
ejb.delete(author);
reset();
}
private void reset() {
authors = null;
authorsAlphanumeric = null;
books = null;
edit = null;
newAuthor = ejb.newAuthor();
newBook = ejb.newBook();
}
}

View File

@ -45,7 +45,7 @@ import static org.jooq.example.db.h2.Tables.AUTHOR;
import static org.jooq.example.db.h2.Tables.BOOK;
import javax.annotation.Resource;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import javax.sql.DataSource;
import org.jooq.Result;
@ -55,20 +55,18 @@ import org.jooq.example.db.h2.tables.records.AuthorRecord;
import org.jooq.example.db.h2.tables.records.BookRecord;
import org.jooq.impl.DSL;
@Stateful
public class AuthorsEJB {
/**
* A session bean that uses the configured {@link DataSource} to interact with
* the embedded H2 database.
*
* @author Lukas Eder
*/
@Stateless
public class LibraryEJB {
@Resource(lookup="java:jboss/datasources/jooq-javaee-example")
private DataSource ds;
public AuthorRecord newAuthor() {
return DSL.using(ds, H2).newRecord(AUTHOR);
}
public BookRecord newBook() {
return DSL.using(ds, H2).newRecord(BOOK);
}
public Result<AuthorRecord> fetchAuthors(SortField<?> sort) {
return DSL.using(ds, H2)
.selectFrom(AUTHOR)
@ -84,10 +82,12 @@ public class AuthorsEJB {
}
public void store(UpdatableRecord<?> record) {
DSL.using(ds, H2).attach(record);
record.store();
}
public void delete(UpdatableRecord<?> record) {
DSL.using(ds, H2).attach(record);
record.delete();
}

View File

@ -1,5 +1,3 @@
DROP TABLE IF EXISTS book_to_book_store;
DROP TABLE IF EXISTS book_store;
DROP TABLE IF EXISTS book;
DROP TABLE IF EXISTS author;
@ -23,43 +21,10 @@ CREATE TABLE book (
CONSTRAINT fk_t_book_author_id FOREIGN KEY (author_id) REFERENCES author(id) ON DELETE CASCADE,
);
CREATE TABLE book_store (
name VARCHAR(400) NOT NULL,
CONSTRAINT uk_t_book_store_name PRIMARY KEY(name)
);
CREATE TABLE book_to_book_store (
book_store_name VARCHAR(400) NOT NULL,
book_id INTEGER NOT NULL,
stock INTEGER,
CONSTRAINT pk_b2bs PRIMARY KEY(book_store_name, book_id),
CONSTRAINT fk_b2bs_bs_name FOREIGN KEY (book_store_name)
REFERENCES book_store (name)
ON DELETE CASCADE,
CONSTRAINT fk_b2bs_b_id FOREIGN KEY (book_id)
REFERENCES book (id)
ON DELETE CASCADE
);
INSERT INTO author VALUES (DEFAULT, 'George', 'Orwell', '1903-06-25');
INSERT INTO author VALUES (DEFAULT, 'Paulo', 'Coelho', '1947-08-24');
INSERT INTO book VALUES (DEFAULT, 1, '1984', 1948, 1);
INSERT INTO book VALUES (DEFAULT, 1, 'Animal Farm', 1945, 1);
INSERT INTO book VALUES (DEFAULT, 2, 'O Alquimista', 1988, 4);
INSERT INTO book VALUES (DEFAULT, 2, 'Brida', 1990, 2);
INSERT INTO book_store (name) VALUES
('Orell Füssli'),
('Ex Libris'),
('Buchhandlung im Volkshaus');
INSERT INTO book_to_book_store VALUES
('Orell Füssli', 1, 10),
('Orell Füssli', 2, 10),
('Orell Füssli', 3, 10),
('Ex Libris', 1, 1),
('Ex Libris', 3, 2),
('Buchhandlung im Volkshaus', 3, 1);
INSERT INTO book VALUES (DEFAULT, 2, 'Brida', 1990, 2);

View File

@ -6,7 +6,7 @@ body {
}
h1, h2, h3 {
padding: 20px;
padding: 10px;
margin: 0;
color: #eee;
}
@ -26,7 +26,7 @@ h3 {
td, th {
text-align: left;
min-width: 50px;
padding: 20px;
padding: 10px;
}
input {
@ -35,7 +35,7 @@ input {
input[type=submit] {
min-width: 50px;
margin-right: 20px;
margin-right: 10px;
}
.div-50 {

View File

@ -19,13 +19,13 @@
<div class="div-50">
<h2>Authors</h2>
<h:dataTable value="#{authors.authors}" var="a">
<h:dataTable value="#{library.authors}" var="a">
<h:column>
<f:facet name="header">
<h:commandLink action="#{authors.sort(authors.authorColumns['ID'])}">
<h:commandLink action="#{library.sort(library.authorColumns['ID'])}">
<h:outputText value="ID"/>
<h:outputText value="⇧" rendered="#{authors.sort['AUTHOR'].name == 'ID' and authors.sort['AUTHOR'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{authors.sort['AUTHOR'].name == 'ID' and authors.sort['AUTHOR'].order == 'DESC'}"/>
<h:outputText value="⇧" rendered="#{library.sort['AUTHOR'].name == 'ID' and library.sort['AUTHOR'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{library.sort['AUTHOR'].name == 'ID' and library.sort['AUTHOR'].order == 'DESC'}"/>
</h:commandLink>
</f:facet>
@ -34,48 +34,48 @@
<h:column>
<f:facet name="header">
<h:commandLink action="#{authors.sort(authors.authorColumns['FIRST_NAME'])}">
<h:commandLink action="#{library.sort(library.authorColumns['FIRST_NAME'])}">
<h:outputText value="First Name"/>
<h:outputText value="⇧" rendered="#{authors.sort['AUTHOR'].name == 'FIRST_NAME' and authors.sort['AUTHOR'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{authors.sort['AUTHOR'].name == 'FIRST_NAME' and authors.sort['AUTHOR'].order == 'DESC'}"/>
<h:outputText value="⇧" rendered="#{library.sort['AUTHOR'].name == 'FIRST_NAME' and library.sort['AUTHOR'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{library.sort['AUTHOR'].name == 'FIRST_NAME' and library.sort['AUTHOR'].order == 'DESC'}"/>
</h:commandLink>
</f:facet>
<h:outputText value="#{a.firstName}" rendered="#{authors.edit != a}"/>
<h:inputText value="#{a.firstName}" rendered="#{authors.edit == a}"/>
<h:outputText value="#{a.firstName}" rendered="#{library.edit != a}"/>
<h:inputText value="#{a.firstName}" rendered="#{library.edit == a}"/>
<f:facet name="footer">
<h:inputText value="#{authors.newAuthor.firstName}"/>
<h:inputText value="#{library.newAuthor.firstName}"/>
</f:facet>
</h:column>
<h:column>
<f:facet name="header">
<h:commandLink action="#{authors.sort(authors.authorColumns['LAST_NAME'])}">
<h:commandLink action="#{library.sort(library.authorColumns['LAST_NAME'])}">
<h:outputText value="Last Name"/>
<h:outputText value="⇧" rendered="#{authors.sort['AUTHOR'].name == 'LAST_NAME' and authors.sort['AUTHOR'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{authors.sort['AUTHOR'].name == 'LAST_NAME' and authors.sort['AUTHOR'].order == 'DESC'}"/>
<h:outputText value="⇧" rendered="#{library.sort['AUTHOR'].name == 'LAST_NAME' and library.sort['AUTHOR'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{library.sort['AUTHOR'].name == 'LAST_NAME' and library.sort['AUTHOR'].order == 'DESC'}"/>
</h:commandLink>
</f:facet>
<h:outputText value="#{a.lastName}" rendered="#{authors.edit != a}"/>
<h:inputText value="#{a.lastName}" rendered="#{authors.edit == a}"/>
<h:outputText value="#{a.lastName}" rendered="#{library.edit != a}"/>
<h:inputText value="#{a.lastName}" rendered="#{library.edit == a}"/>
<f:facet name="footer">
<h:inputText value="#{authors.newAuthor.lastName}"/>
<h:inputText value="#{library.newAuthor.lastName}"/>
</f:facet>
</h:column>
<h:column>
<f:facet name="header">Actions</f:facet>
<h:commandButton value="edit" action="#{authors.edit(a)}" rendered="#{authors.edit != a}"/>
<h:commandButton value="save" action="#{authors.save(a)}" rendered="#{authors.edit == a}"/>
<h:commandButton value="edit" action="#{library.edit(a)}" rendered="#{library.edit != a}"/>
<h:commandButton value="save" action="#{library.save(a)}" rendered="#{library.edit == a}"/>
<h:commandButton value="delete" action="#{authors.delete(a)}"/>
<h:commandButton value="delete" action="#{library.delete(a)}"/>
<f:facet name="footer">
<h:commandButton value="create" action="#{authors.save(authors.newAuthor)}"/>
<h:commandButton value="create" action="#{library.save(library.newAuthor)}"/>
</f:facet>
</h:column>
</h:dataTable>
@ -84,13 +84,13 @@
<div class="div-50">
<h2>Books</h2>
<h:dataTable value="#{authors.books}" var="b">
<h:dataTable value="#{library.books}" var="b">
<h:column>
<f:facet name="header">
<h:commandLink action="#{authors.sort(authors.bookColumns['ID'])}">
<h:commandLink action="#{library.sort(library.bookColumns['ID'])}">
<h:outputText value="ID"/>
<h:outputText value="⇧" rendered="#{authors.sort['BOOK'].name == 'ID' and authors.sort['BOOK'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{authors.sort['BOOK'].name == 'ID' and authors.sort['BOOK'].order == 'DESC'}"/>
<h:outputText value="⇧" rendered="#{library.sort['BOOK'].name == 'ID' and library.sort['BOOK'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{library.sort['BOOK'].name == 'ID' and library.sort['BOOK'].order == 'DESC'}"/>
</h:commandLink>
</f:facet>
@ -100,45 +100,45 @@
<h:column>
<f:facet name="header">Author</f:facet>
<h:outputText value="#{authors.authorById[b.authorId].firstName} #{authors.authorById[b.authorId].lastName}" rendered="#{authors.edit != b}"/>
<h:selectOneMenu value="#{b.authorId}" rendered="#{authors.edit == b}">
<f:selectItems value="#{authors.authorsAlphanumeric}" var="a" itemLabel="#{a.firstName} #{a.lastName}" itemValue="#{a.id}" />
<h:outputText value="#{library.authorById[b.authorId].firstName} #{library.authorById[b.authorId].lastName}" rendered="#{library.edit != b}"/>
<h:selectOneMenu value="#{b.authorId}" rendered="#{library.edit == b}">
<f:selectItems value="#{library.authorsAlphanumeric}" var="a" itemLabel="#{a.firstName} #{a.lastName}" itemValue="#{a.id}" />
</h:selectOneMenu>
<f:facet name="footer">
<h:selectOneMenu value="#{authors.newBook.authorId}">
<f:selectItems value="#{authors.authorsAlphanumeric}" var="a" itemLabel="#{a.firstName} #{a.lastName}" itemValue="#{a.id}" />
<h:selectOneMenu value="#{library.newBook.authorId}">
<f:selectItems value="#{library.authorsAlphanumeric}" var="a" itemLabel="#{a.firstName} #{a.lastName}" itemValue="#{a.id}" />
</h:selectOneMenu>
</f:facet>
</h:column>
<h:column>
<f:facet name="header">
<h:commandLink action="#{authors.sort(authors.bookColumns['TITLE'])}">
<h:commandLink action="#{library.sort(library.bookColumns['TITLE'])}">
<h:outputText value="Title"/>
<h:outputText value="⇧" rendered="#{authors.sort['BOOK'].name == 'TITLE' and authors.sort['BOOK'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{authors.sort['BOOK'].name == 'TITLE' and authors.sort['BOOK'].order == 'DESC'}"/>
<h:outputText value="⇧" rendered="#{library.sort['BOOK'].name == 'TITLE' and library.sort['BOOK'].order == 'ASC'}"/>
<h:outputText value="⇩" rendered="#{library.sort['BOOK'].name == 'TITLE' and library.sort['BOOK'].order == 'DESC'}"/>
</h:commandLink>
</f:facet>
<h:outputText value="#{b.title}" rendered="#{authors.edit != b}"/>
<h:inputText value="#{b.title}" rendered="#{authors.edit == b}"/>
<h:outputText value="#{b.title}" rendered="#{library.edit != b}"/>
<h:inputText value="#{b.title}" rendered="#{library.edit == b}"/>
<f:facet name="footer">
<h:inputText value="#{authors.newBook.title}"/>
<h:inputText value="#{library.newBook.title}"/>
</f:facet>
</h:column>
<h:column>
<f:facet name="header">Actions</f:facet>
<h:commandButton value="edit" action="#{authors.edit(b)}" rendered="#{authors.edit != b}"/>
<h:commandButton value="save" action="#{authors.save(b)}" rendered="#{authors.edit == b}"/>
<h:commandButton value="edit" action="#{library.edit(b)}" rendered="#{library.edit != b}"/>
<h:commandButton value="save" action="#{library.save(b)}" rendered="#{library.edit == b}"/>
<h:commandButton value="delete" action="#{authors.delete(b)}"/>
<h:commandButton value="delete" action="#{library.delete(b)}"/>
<f:facet name="footer">
<h:commandButton value="create" action="#{authors.save(authors.newBook)}"/>
<h:commandButton value="create" action="#{library.save(library.newBook)}"/>
</f:facet>
</h:column>
</h:dataTable>