Remove outdated jOOQ-javaee-example

It's not part of the jOOQ-examples build, and the various dependency versions are heavily outdated. It's not even called JavaEE anymore, but JakartaEE now. If someone asks for it, we can create a new example...
This commit is contained in:
Lukas Eder 2021-12-16 16:17:19 +01:00
parent da302ef7b8
commit b702b36439
17 changed files with 0 additions and 961 deletions

View File

@ -1,2 +0,0 @@
/target
/*.iml

View File

@ -1,19 +0,0 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Other licenses:
-----------------------------------------------------------------------------
Commercial licenses for this work are available. These replace the above
ASL 2.0 and offer limited warranties, support, maintenance, and commercial
database integrations.
For more information, please visit: http://www.jooq.org/licenses

View File

@ -1,26 +0,0 @@
Thanks for downloading jOOQ.
Please visit http://www.jooq.org for more information.
To install and run this example, simply check it out and run the following Maven command
```
$ pwd
/path/to/checkout/dir
$ 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.
For more information about how to setup a WildFly project using EJB, please visit the WildFly Quickstart projects, e.g.:
https://github.com/wildfly/quickstart/tree/master/ejb-in-war

View File

@ -1,235 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jooq</groupId>
<artifactId>jooq-examples</artifactId>
<version>3.16.0-SNAPSHOT</version>
</parent>
<artifactId>jooq-javaee-example</artifactId>
<packaging>war</packaging>
<name>jOOQ JavaEE Example</name>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.jooq.org/inc/LICENSE.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.h2.version>1.4.199</org.h2.version>
<java.version>1.8</java.version>
<!-- JBoss dependency versions -->
<version.wildfly.maven.plugin>1.0.2.Final</version.wildfly.maven.plugin>
<version.jboss.bom>8.0.0.Final</version.jboss.bom>
<wildfly-port>10990</wildfly-port>
<wildfly-username>admin</wildfly-username>
<wildfly-password>admin</wildfly-password>
<!-- other plugin versions -->
<version.surefire.plugin>2.10</version.surefire.plugin>
<version.war.plugin>2.1.1</version.war.plugin>
</properties>
<dependencyManagement>
<dependencies>
<!-- Define the version of JBoss' Java EE 7 APIs we want to import.
Any dependencies from org.jboss.spec will have their version defined by this
BOM -->
<!-- JBoss distributes a complete set of Java EE 7 APIs including
a Bill of Materials (BOM). A BOM specifies the versions of a "stack" (or
a collection) of artifacts. We use this here so that we always get the correct
versions of artifacts. Here we use the jboss-javaee-7.0-with-tools stack
(you can read this as the JBoss stack of the Java EE 7 APIs, with some extras
tools for your project, such as Arquillian for testing) and the jboss-javaee-7.0-with-hibernate
stack you can read this as the JBoss stack of the Java EE 7 APIs, with extras
from the Hibernate family of projects) -->
<dependency>
<groupId>org.wildfly.bom</groupId>
<artifactId>jboss-javaee-7.0-with-tools</artifactId>
<version>${version.jboss.bom}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Database access -->
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<!-- Wildfly and JavaEE -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.annotation</groupId>
<artifactId>jboss-annotations-api_1.2_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.faces</groupId>
<artifactId>jboss-jsf-api_2.2_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ejb</groupId>
<artifactId>jboss-ejb-api_3.2_spec</artifactId>
<scope>provided</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j18-impl</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${version.war.plugin}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>11</release>
<!-- IntelliJ needs these https://youtrack.jetbrains.com/issue/IDEA-195472 -->
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<!-- We're using the properties plugin to load external properties into Maven.
See this excellent blog post for an explanation:
http://www.petrikainulainen.net/programming/tips-and-tricks/creating-profile-specific-configuration-files-with-maven/ -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>src/main/resources/config.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
<!-- The H2 test schema is loaded here -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>sql-maven-plugin</artifactId>
<configuration>
<skip>${maven.test.skip}</skip>
<forkMode>always</forkMode>
</configuration>
<executions>
<execution>
<id>create-database-h2</id>
<phase>generate-sources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<driver>${db.driver}</driver>
<url>${db.url}</url>
<username>${db.username}</username>
<password>${db.password}</password>
<autocommit>true</autocommit>
<srcFiles>
<srcFile>src/main/resources/db-h2.sql</srcFile>
</srcFiles>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${org.h2.version}</version>
</dependency>
</dependencies>
</plugin>
<!-- The jOOQ code generator plugin -->
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<executions>
<execution>
<id>generate-h2</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<jdbc>
<driver>${db.driver}</driver>
<url>${db.url}</url>
<user>${db.username}</user>
<password>${db.password}</password>
</jdbc>
<generator>
<database>
<includes>.*</includes>
<excludes></excludes>
<inputSchema>PUBLIC</inputSchema>
</database>
<target>
<packageName>org.jooq.example.db.h2</packageName>
<directory>target/generated-sources/jooq-h2</directory>
</target>
</generator>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,257 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.example.javaee.controller;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toMap;
import static org.jooq.SQLDialect.H2;
import static org.jooq.SortOrder.ASC;
import static org.jooq.example.db.h2.tables.Author.AUTHOR;
import static org.jooq.example.db.h2.tables.Book.BOOK;
import java.io.Serializable;
import java.util.Map;
import java.util.stream.Stream;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Result;
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.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("library")
@SessionScoped
public class Library implements Serializable {
private static final long serialVersionUID = 1L;
@EJB
private LibraryEJB ejb;
// 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));
return authors;
}
public Result<AuthorRecord> getAuthorsAlphanumeric() {
if (authorsAlphanumeric == null) {
authorsAlphanumeric = DSL.using(H2).newResult(AUTHOR);
authorsAlphanumeric.addAll(getAuthors());
authorsAlphanumeric.sortAsc(comparing(a -> a.getFirstName() + " " + a.getLastName()));
}
return authorsAlphanumeric;
}
public Map<Integer, AuthorRecord> getAuthorById() {
return getAuthors().intoMap(AUTHOR.ID);
}
public Result<BookRecord> getBooks() {
if (books == null)
books = ejb.fetchBooks(sort.get(BOOK));
return books;
}
public AuthorRecord getNewAuthor() {
if (newAuthor == null)
newAuthor = DSL.using(H2).newRecord(AUTHOR);
return newAuthor;
}
public BookRecord getNewBook() {
if (newBook == null)
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(),
! previous.getName().equals(field.getName())
? field.asc()
: previous.getOrder() == ASC
? field.desc()
: field.asc()
);
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();
}
}

View File

@ -1,91 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.example.javaee.ejb;
import static org.jooq.SQLDialect.H2;
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.Stateless;
import javax.sql.DataSource;
import org.jooq.Result;
import org.jooq.SortField;
import org.jooq.UpdatableRecord;
import org.jooq.example.db.h2.tables.records.AuthorRecord;
import org.jooq.example.db.h2.tables.records.BookRecord;
import org.jooq.impl.DSL;
/**
* 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 Result<AuthorRecord> fetchAuthors(SortField<?> sort) {
return DSL.using(ds, H2)
.selectFrom(AUTHOR)
.orderBy(sort)
.fetch();
}
public Result<BookRecord> fetchBooks(SortField<?> sort) {
return DSL.using(ds, H2)
.selectFrom(BOOK)
.orderBy(sort)
.fetch();
}
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,19 +0,0 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Other licenses:
-----------------------------------------------------------------------------
Commercial licenses for this work are available. These replace the above
ASL 2.0 and offer limited warranties, support, maintenance, and commercial
database integrations.
For more information, please visit: http://www.jooq.org/licenses

View File

@ -1,2 +0,0 @@
Thanks for downloading jOOQ.
Please visit http://www.jooq.org for more information

View File

@ -1,11 +0,0 @@
#Database Configuration
db.driver=org.h2.Driver
db.url=jdbc:h2:~/jooq-javaee-example
db.username=sa
db.password=
#jOOQ Configuration
jooq.sql.dialect=H2
#DB Schema
db.schema.script=db-h2.sql

View File

@ -1,30 +0,0 @@
DROP TABLE IF EXISTS book;
DROP TABLE IF EXISTS author;
CREATE TABLE author (
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(50),
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE,
CONSTRAINT pk_t_author PRIMARY KEY (ID)
);
CREATE TABLE book (
id INT NOT NULL AUTO_INCREMENT,
author_id INT NOT NULL,
title VARCHAR(400) NOT NULL,
published_in INT,
language_id INT,
CONSTRAINT pk_t_book PRIMARY KEY (id),
CONSTRAINT fk_t_book_author_id FOREIGN KEY (author_id) REFERENCES author(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);

View File

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ABSOLUTE} %5p [%-50c{4}] - %m%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.springframework.core" level="info">
<AppenderRef ref="Console"/>
</Logger>
<Logger name="org.springframework.beans" level="info">
<AppenderRef ref="Console"/>
</Logger>
<Logger name="org.springframework.context" level="info">
<AppenderRef ref="Console"/>
</Logger>
<Logger name="org.springframework.web" level="info">
<AppenderRef ref="Console"/>
</Logger>
<Logger name="org.jooq.impl" level="debug">
<AppenderRef ref="Console"/>
</Logger>
<Root level="debug">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
</beans>

View File

@ -1,8 +0,0 @@
<?xml version="1.0"?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
</faces-config>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
In this example project, we're not using a managed data source to make sure
the example runs out of the box.
In a production environment, we obviously wouldn't use an unmanaged data
source that connects to a single-connection embedded H2 database
-->
<datasources xmlns="http://www.jboss.org/ironjacamar/schema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">
<datasource jndi-name="java:jboss/datasources/jooq-javaee-example"
pool-name="jooq-javaee-example" enabled="true"
use-java-context="true">
<connection-url>jdbc:h2:~/jooq-javaee-example</connection-url>
<driver-class>org.h2.Driver</driver-class>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password></password>
</security>
</datasource>
</datasources>

View File

@ -1,44 +0,0 @@
body {
margin: 0;
padding: 0;
font-family: 'Open Sans', sans-serif;
}
h1, h2, h3 {
padding: 10px;
margin: 0;
color: #eee;
}
h1 {
background-color: #000;
}
h2 {
background-color: #222;
}
h3 {
background-color: #444;
}
td, th {
text-align: left;
min-width: 50px;
padding: 10px;
}
input {
min-width: 50px;
}
input[type=submit] {
min-width: 50px;
margin-right: 10px;
}
.div-50 {
width: 50%;
float: left;
}

View File

@ -1,5 +0,0 @@
<html>
<head>
<meta http-equiv="Refresh" content="0; URL=library.jsf">
</head>
</html>

View File

@ -1,149 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300" rel="stylesheet" type="text/css"></link>
<link href="css/example.css" type="text/css" rel="stylesheet"></link>
<title>Library</title>
</head>
<body>
<h1>Library</h1>
<div id="container">
<h:form id="response">
<div class="div-50">
<h2>Authors</h2>
<h:dataTable value="#{library.authors}" var="a">
<h:column>
<f:facet name="header">
<h:commandLink action="#{library.sort(library.authorColumns['ID'])}">
<h:outputText value="ID"/>
<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>
<h:outputText value="#{a.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:commandLink action="#{library.sort(library.authorColumns['FIRST_NAME'])}">
<h:outputText value="First Name"/>
<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="#{library.edit != a}"/>
<h:inputText value="#{a.firstName}" rendered="#{library.edit == a}"/>
<f:facet name="footer">
<h:inputText value="#{library.newAuthor.firstName}"/>
</f:facet>
</h:column>
<h:column>
<f:facet name="header">
<h:commandLink action="#{library.sort(library.authorColumns['LAST_NAME'])}">
<h:outputText value="Last Name"/>
<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="#{library.edit != a}"/>
<h:inputText value="#{a.lastName}" rendered="#{library.edit == a}"/>
<f:facet name="footer">
<h:inputText value="#{library.newAuthor.lastName}"/>
</f:facet>
</h:column>
<h:column>
<f:facet name="header">Actions</f:facet>
<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="#{library.delete(a)}"/>
<f:facet name="footer">
<h:commandButton value="create" action="#{library.save(library.newAuthor)}"/>
</f:facet>
</h:column>
</h:dataTable>
</div>
<div class="div-50">
<h2>Books</h2>
<h:dataTable value="#{library.books}" var="b">
<h:column>
<f:facet name="header">
<h:commandLink action="#{library.sort(library.bookColumns['ID'])}">
<h:outputText value="ID"/>
<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>
<h:outputText value="#{b.id}"/>
</h:column>
<h:column>
<f:facet name="header">Author</f:facet>
<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="#{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="#{library.sort(library.bookColumns['TITLE'])}">
<h:outputText value="Title"/>
<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="#{library.edit != b}"/>
<h:inputText value="#{b.title}" rendered="#{library.edit == b}"/>
<f:facet name="footer">
<h:inputText value="#{library.newBook.title}"/>
</f:facet>
</h:column>
<h:column>
<f:facet name="header">Actions</f:facet>
<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="#{library.delete(b)}"/>
<f:facet name="footer">
<h:commandButton value="create" action="#{library.save(library.newBook)}"/>
</f:facet>
</h:column>
</h:dataTable>
</div>
</h:form>
</div>
</body>
</html>