Add Spring Boot example
diff --git a/empire-db-examples/empire-db-example-spring-boot/pom.xml b/empire-db-examples/empire-db-example-spring-boot/pom.xml
new file mode 100644
index 0000000..8edea72
--- /dev/null
+++ b/empire-db-examples/empire-db-example-spring-boot/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.springframework.boot</groupId>
+    <artifactId>spring-boot-starter-parent</artifactId>
+    <version>2.5.3</version>
+    <relativePath/> <!-- lookup parent from repository -->
+  </parent>
+  
+  <name>Apache Empire-db Spring Boot Example</name>
+  <artifactId>empire-db-example-spring-boot</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <!-- The spring-boot-starter-parent chooses fairly conservative Java compatibility. If you want to follow our recommendation and use a later Java version you can add a java.version property -->
+    <java.version>11</java.version>
+    <maven.compiler.source>11</maven.compiler.source>
+    <maven.compiler.target>11</maven.compiler.target>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.empire-db</groupId>
+      <artifactId>empire-db</artifactId>
+      <version>2.5.1-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-jdbc</artifactId>
+    </dependency>
+    
+    <!-- This dependency should be declared as runtime but the exec plugin is broken atm... -->
+    <dependency>
+      <groupId>org.hsqldb</groupId>
+      <artifactId>hsqldb</artifactId>
+      <scope>runtime</scope>
+    </dependency> 
+    <!--    <dependency>
+      <groupId>postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+      <version>8.3-603.jdbc3</version>
+      <scope>runtime</scope>
+    </dependency> -->
+    <!--    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <scope>runtime</scope>
+    </dependency> -->
+    <!--    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derby</artifactId>
+      <scope>runtime</scope>
+    </dependency> -->
+    <!--    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <version>5.1.6</version>
+      <scope>runtime</scope>
+    </dependency>-->
+    <!-- msssql -->
+    <!--    <dependency>
+      <groupId>com.microsoft.sqlserver</groupId>
+      <artifactId>mssql-jdbc</artifactId>
+      <version>7.0.0.jre8</version>
+      <scope>runtime</scope>
+    </dependency>-->
+    <!-- ojdbc 
+    <dependency>
+      <groupId>com.oracle</groupId>
+      <artifactId>ojdbc6</artifactId>
+      <version>11.2.0.3.0</version>
+      <scope>runtime</scope>
+    </dependency>
+    -->
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <!-- Package as an executable jar -->
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/EmpireDBConfigProperties.java b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/EmpireDBConfigProperties.java
new file mode 100644
index 0000000..c296d1b
--- /dev/null
+++ b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/EmpireDBConfigProperties.java
@@ -0,0 +1,29 @@
+package org.apache.empire.samples.springboot;
+
+import java.util.Map;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "empiredb")
+public class EmpireDBConfigProperties {
+
+  private String driverClass;
+  private Map<String, String> driverProperties;
+
+  public String getDriverClass() {
+    return driverClass;
+  }
+
+  public Map<String, String> getDriverProperties() {
+    return driverProperties;
+  }
+
+  public void setDriverClass(String driverClass) {
+    this.driverClass = driverClass;
+  }
+
+  public void setDriverProperties(Map<String, String> driverProperties) {
+    this.driverProperties = driverProperties;
+  }
+}
diff --git a/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleApp.java b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleApp.java
new file mode 100644
index 0000000..a28ba43
--- /dev/null
+++ b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleApp.java
@@ -0,0 +1,453 @@
+package org.apache.empire.samples.springboot;
+
+import java.lang.reflect.InvocationTargetException;
+import java.sql.Connection;
+import java.util.List;
+import java.util.Map;
+import javax.sql.DataSource;
+import org.apache.commons.beanutils.BeanUtils;
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.data.bean.BeanResult;
+import org.apache.empire.db.DBColumnExpr;
+import org.apache.empire.db.DBCommand;
+import org.apache.empire.db.DBDatabaseDriver;
+import org.apache.empire.db.DBReader;
+import org.apache.empire.db.DBRecord;
+import org.apache.empire.db.DBSQLScript;
+import org.apache.empire.db.derby.DBDatabaseDriverDerby;
+import org.apache.empire.db.h2.DBDatabaseDriverH2;
+import org.apache.empire.db.hsql.DBDatabaseDriverHSql;
+import org.apache.empire.db.postgresql.DBDatabaseDriverPostgreSQL;
+import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.samples.springboot.SampleDB.Gender;
+import org.apache.empire.xml.XMLWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.core.env.Environment;
+import org.w3c.dom.Document;
+
+/**
+ * Implementing ApplicationRunner interface tells Spring Boot to automatically call the run method AFTER the application context has been loaded.
+ */
+@SpringBootApplication
+public class SampleApp implements ApplicationRunner {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(SampleApp.class);
+
+  @Autowired
+  private EmpireDBConfigProperties empireDBConfigProperties;
+
+  @Autowired
+  private DataSource dataSource;
+
+  @Autowired
+  private Environment environment;
+
+  @Autowired
+  private SampleDB db;
+
+  private enum QueryType {
+    Reader,
+    BeanList,
+    XmlDocument
+  }
+
+  public static void main(String[] args) {
+    SpringApplication.run(SampleApp.class, args);
+  }
+
+  @Override
+  public void run(ApplicationArguments args) throws Exception {
+    LOGGER.info("STARTING THE APPLICATION");
+
+    System.out.println("Running DB Sample...");
+
+    // STEP 1: Get a JDBC Connection
+    System.out.println("*** Step 1: getJDBCConnection() ***");
+    Connection conn = getJDBCConnection();
+
+    // STEP 2: Choose a driver
+    System.out.println("*** Step 2: getDatabaseDriver() ***");
+    DBDatabaseDriver driver = getDatabaseDriver(conn);
+
+    try {
+      // STEP 3: Open Database (and create if not existing)
+      System.out.println("*** Step 3: openDatabase() ***");
+      // Open the database
+      db.open(driver, conn);
+      // Check whether database exists
+      databaseExists(conn);
+      System.out.println("*** Database already exists. Skipping Step4 ***");
+    } catch (Exception e) {
+      // STEP 4: Create Database
+      System.out.println("*** Step 4: createDDL() ***");
+      // postgre does not support DDL in transaction
+      if (db.getDriver() instanceof DBDatabaseDriverPostgreSQL) {
+        conn.setAutoCommit(true);
+      }
+      createDatabase(driver, conn);
+      if (db.getDriver() instanceof DBDatabaseDriverPostgreSQL) {
+        conn.setAutoCommit(false);
+      }
+      // Open again
+      if (db.isOpen() == false) {
+        db.open(driver, conn);
+      }
+
+      // STEP 5: Clear Database (Delete all records)
+      System.out.println("*** Step 5: clearDatabase() ***");
+      clearDatabase(conn);
+
+      // STEP 6: Insert Departments
+      System.out.println("*** Step 6: insertDepartment() & insertEmployee() ***");
+      int idDevDep = insertDepartment(conn, "Development", "ITTK");
+      int idSalDep = insertDepartment(conn, "Sales", "ITTK");
+      // Insert Employees
+      int idPers1 = insertEmployee(conn, "Peter", "Sharp", Gender.M, idDevDep);
+      int idPers2 = insertEmployee(conn, "Fred", "Bloggs", Gender.M, idDevDep);
+      int idPers3 = insertEmployee(conn, "Emma", "White", Gender.F, idSalDep);
+
+      // STEP 7: Update Records (by setting the phone Number)
+      System.out.println("*** Step 7: updateEmployee() ***");
+      updateEmployee(conn, idPers1, "+49-7531-457160");
+      updateEmployee(conn, idPers2, "+49-5555-505050");
+      updateEmployee(conn, idPers3, "+49-040-125486");
+
+      // commit
+      db.commit(conn);
+
+      // STEP 8: Option 1: Query Records and print tab-separated
+      System.out.println("*** Step 8 Option 1: queryRecords() / Tab-Output ***");
+      queryRecords(conn, QueryType.Reader); // Tab-Output
+
+      // STEP 8: Option 2: Query Records as a list of java beans
+      System.out.println("*** Step 8 Option 2: queryRecords() / Bean-List-Output ***");
+      queryRecords(conn, QueryType.BeanList); // Bean-List-Output
+
+      // STEP 8: Option 3: Query Records as XML
+      System.out.println("*** Step 8 Option 3: queryRecords() / XML-Output ***");
+      queryRecords(conn, QueryType.XmlDocument); // XML-Output
+
+      // STEP 9: Use Bean Result to query beans
+      queryBeans(conn);
+
+      // Done
+      System.out.println("DB Sample finished successfully.");
+    }
+
+  }
+
+  /**
+   * Creates an Empire-db DatabaseDriver for the given provider and applies driver specific configuration
+   */
+  private DBDatabaseDriver getDatabaseDriver(Connection conn) {
+    try {   // Get Driver Class Name
+      String driverClassName = empireDBConfigProperties.getDriverClass();
+      if (StringUtils.isEmpty(driverClassName)) {
+        throw new RuntimeException("Configuration error: Element 'empiredb.driverClass' not found in properties of profile '" + environment.getActiveProfiles().toString() + "'");
+      }
+
+      // Create driver
+      DBDatabaseDriver driver = (DBDatabaseDriver) Class.forName(driverClassName).getDeclaredConstructor().newInstance();
+
+      // Configure driver
+      readProperties(driver);
+
+      // Special cases
+      if (driver instanceof DBDatabaseDriverPostgreSQL) {
+        // Create the reverse function that is needed by this sample
+        ((DBDatabaseDriverPostgreSQL) driver).createReverseFunction(conn);
+      }
+
+      // done
+      return driver;
+    } catch (Exception e) {
+      // catch any checked exception and forward it
+      e.printStackTrace();
+      throw new RuntimeException(e);
+    }
+  }
+
+  /**
+   * <PRE>
+   * Empties all Tables.
+   * </PRE>
+   */
+  private void clearDatabase(Connection conn) {
+    DBCommand cmd = db.createCommand();
+    // Delete all Employees (no constraints)
+    db.executeDelete(db.EMPLOYEES, cmd, conn);
+    // Delete all Departments (no constraints)
+    db.executeDelete(db.DEPARTMENTS, cmd, conn);
+  }
+
+  /**
+   * <PRE>
+   * Creates a DDL Script for entire SampleDB Database and executes it line by line.
+   * Please make sure you uses the correct DatabaseDriver for your target DBMS.
+   * </PRE>
+   */
+  private void createDatabase(DBDatabaseDriver driver, Connection conn) {
+    // create DDL for Database Definition
+    DBSQLScript script = new DBSQLScript();
+    db.getCreateDDLScript(driver, script);
+    // Show DDL Statement
+    System.out.println(script.toString());
+    // Execute Script
+    script.executeAll(driver, conn, false);
+    // Commit
+    db.commit(conn);
+  }
+
+  /**
+   * <PRE>
+   * Checks whether the database exists or not by executing
+   *     select count(*) from DEPARTMENTS
+   * If the Departments table does not exist the querySingleInt() function return -1 for failure.
+   * Please note that in this case an error will appear in the log which can be ignored.
+   * </PRE>
+   */
+  private boolean databaseExists(Connection conn) {
+    // Check whether DB exists
+    DBCommand cmd = db.createCommand();
+    cmd.select(db.DEPARTMENTS.count());
+    // Check using "select count(*) from DEPARTMENTS"
+    System.out.println("Checking whether table DEPARTMENTS exists (SQLException will be logged if not - please ignore) ...");
+    return (db.querySingleInt(cmd, -1, conn) >= 0);
+  }
+
+  /**
+   * <PRE>
+   * Opens and returns a JDBC-Connection.
+   * JDBC url, user and password for the connection are obtained from the SampleConfig bean
+   * Please use the config.xml file to change connection params.
+   * </PRE>
+   */
+  private Connection getJDBCConnection() {
+    // Establish a new database connection
+    Connection conn = null;
+    try {
+      conn = dataSource.getConnection();
+      LOGGER.info("Connected successfully");
+      // set the AutoCommit to false for this connection. 
+      // commit must be called explicitly! 
+      conn.setAutoCommit(false);
+      LOGGER.info("AutoCommit is " + conn.getAutoCommit());
+    } catch (Exception e) {
+//      LOGGER.error("Failed to connect directly to '" + config.getJdbcURL() + "' / User=" + config.getJdbcUser());
+      LOGGER.error(e.toString());
+      throw new RuntimeException(e);
+    }
+    return conn;
+  }
+
+  private void readProperties(Object bean) {
+    // Check arguments
+    if (bean == null) {
+      throw new InvalidArgumentException("bean", bean);
+    }
+
+    Map<String, String> driverProperties = empireDBConfigProperties.getDriverProperties();
+    if (driverProperties != null) {
+      for (Map.Entry<String, String> entry : driverProperties.entrySet()) {
+        String name = entry.getKey();
+        String newValue = entry.getValue();
+        try {
+          BeanUtils.setProperty(bean, name, newValue);
+
+          Object value = BeanUtils.getProperty(bean, name);
+          if (ObjectUtils.compareEqual(newValue, value)) {
+            LOGGER.info("Configuration property '{}' set to \"{}\"", name, newValue);
+          } else {
+            LOGGER.error("Failed to set property '{}'. Value is \"{}\"", name, value);
+          }
+        } catch (IllegalAccessException | InvocationTargetException ex) {
+          LOGGER.error(null, ex);
+        } catch (NoSuchMethodException ex) {
+          LOGGER.error("Property '{}' not found in {}", name, bean.getClass().getName());
+        }
+      }
+    }
+  }
+
+  /**
+   * <PRE>
+   * Insert a Department into the Departments table.
+   * </PRE>
+   */
+  private int insertDepartment(Connection conn, String departmentName, String businessUnit) {
+    // Insert a Department
+    DBRecord rec = new DBRecord();
+    rec.create(db.DEPARTMENTS);
+    rec.setValue(db.DEPARTMENTS.NAME, departmentName);
+    rec.setValue(db.DEPARTMENTS.BUSINESS_UNIT, businessUnit);
+    rec.update(conn);
+    // Return Department ID
+    return rec.getInt(db.DEPARTMENTS.DEPARTMENT_ID);
+  }
+
+  /**
+   * <PRE>
+   * Inserts an Employee into the Employees table.
+   * </PRE>
+   */
+  private int insertEmployee(Connection conn, String firstName, String lastName, Gender gender, int departmentId) {
+    // Insert an Employee
+    DBRecord rec = new DBRecord();
+    rec.create(db.EMPLOYEES);
+    rec.setValue(db.EMPLOYEES.FIRSTNAME, firstName);
+    rec.setValue(db.EMPLOYEES.LASTNAME, lastName);
+    rec.setValue(db.EMPLOYEES.GENDER, gender);
+    rec.setValue(db.EMPLOYEES.DEPARTMENT_ID, departmentId);
+    rec.update(conn);
+    // Return Employee ID
+    return rec.getInt(db.EMPLOYEES.EMPLOYEE_ID);
+  }
+
+  /**
+   * <PRE>
+   * Updates an employee record by setting the phone number.
+   * </PRE>
+   */
+  private void updateEmployee(Connection conn, int idPers, String phoneNumber) {
+    // Update an Employee
+    DBRecord rec = new DBRecord();
+    rec.read(db.EMPLOYEES, idPers, conn);
+    // Set
+    rec.setValue(db.EMPLOYEES.PHONE_NUMBER, phoneNumber);
+    rec.update(conn);
+  }
+
+  /**
+   * <PRE>
+   * Performs an SQL-Query and prints the result to System.out
+   *
+   * First a DBCommand object is used to create the following SQL-Query (Oracle-Syntax):
+   *
+   *   SELECT t2.EMPLOYEE_ID, t2.LASTNAME || ', ' || t2.FIRSTNAME AS FULL_NAME, t2.GENDER, t2.PHONE_NUMBER,
+   *          substr(t2.PHONE_NUMBER, length(t2.PHONE_NUMBER)-instr(reverse(t2.PHONE_NUMBER), '-')+2) AS PHONE_EXTENSION,
+   *          t1.NAME AS DEPARTMENT, t1.BUSINESS_UNIT
+   *   FROM EMPLOYEES t2 INNER JOIN DEPARTMENTS t1 ON t1.DEPARTMENT_ID = t2.DEPARTMENT_ID
+   *   WHERE length(t2.LASTNAME)>0
+   *   ORDER BY t2.LASTNAME, t2.FIRSTNAME
+   *
+   * For processing the rows there are three options available:
+   *
+   *   QueryType.Reader:
+   *     Iterates through all rows and prints field values as tabbed text.
+   *
+   *   QueryType.BeanList:
+   *     Obtains the query result as a list of JavaBean objects of type SampleBean.
+   *     It then iterates through the list of beans and uses bean.toString() for printing.
+   *
+   *   QueryType.XmlDocument:
+   *     Obtains the query result as an XML-Document and prints the document.
+   *     Please note, that the XML not only contains the data but also the field metadata.
+   * </PRE>
+   */
+  private void queryRecords(Connection conn, QueryType queryType) {
+    // Define the query
+    DBCommand cmd = db.createCommand();
+    // Define shortcuts for tables used - not necessary but convenient
+    SampleDB.Employees EMP = db.EMPLOYEES;
+    SampleDB.Departments DEP = db.DEPARTMENTS;
+
+    // The following expression concats lastname + ', ' + firstname
+    DBColumnExpr EMPLOYEE_FULLNAME = EMP.LASTNAME.append(", ").append(EMP.FIRSTNAME).as("FULL_NAME");
+
+    // The following expression extracts the extension number from the phone field
+    // e.g. substr(PHONE_NUMBER, length(PHONE_NUMBER)-instr(reverse(PHONE_NUMBER), '-')+2) AS PHONE_EXTENSION
+    // Hint: Since the reverse() function is not supported by HSQLDB there is special treatment for HSQL
+    DBColumnExpr PHONE_LAST_DASH;
+    if (db.getDriver() instanceof DBDatabaseDriverHSql
+            || db.getDriver() instanceof DBDatabaseDriverDerby
+            || db.getDriver() instanceof DBDatabaseDriverH2) {
+      PHONE_LAST_DASH = EMP.PHONE_NUMBER.indexOf("-", EMP.PHONE_NUMBER.indexOf("-").plus(1)).plus(1); // HSQLDB only
+    } else {
+      PHONE_LAST_DASH = EMP.PHONE_NUMBER.length().minus(EMP.PHONE_NUMBER.reverse().indexOf("-")).plus(2);
+    }
+    DBColumnExpr PHONE_EXT_NUMBER = EMP.PHONE_NUMBER.substring(PHONE_LAST_DASH).as("PHONE_EXTENSION");
+
+    // DBColumnExpr genderExpr = cmd.select(EMP.GENDER.decode(EMP.GENDER.getOptions()).as(EMP.GENDER.getName()));
+    // Select required columns
+    cmd.select(EMP.EMPLOYEE_ID, EMPLOYEE_FULLNAME);
+    cmd.select(EMP.GENDER, EMP.PHONE_NUMBER, PHONE_EXT_NUMBER);
+    cmd.select(DEP.NAME.as("DEPARTMENT"));
+    cmd.select(DEP.BUSINESS_UNIT);
+    cmd.join(EMP.DEPARTMENT_ID, DEP.DEPARTMENT_ID);
+    // Set constraints and order
+    cmd.where(EMP.LASTNAME.length().isGreaterThan(0));
+    cmd.orderBy(EMP.LASTNAME, EMP.FIRSTNAME);
+
+    /*
+        // Example for limitRows() and skipRows()
+        if (db.getDriver().isSupported(DBDriverFeature.QUERY_LIMIT_ROWS))
+        {	// set maximum number of rows
+        	cmd.limitRows(20);
+            if (db.getDriver().isSupported(DBDriverFeature.QUERY_SKIP_ROWS))
+                cmd.skipRows(1);
+        }
+     */
+    // Query Records and print output
+    DBReader reader = new DBReader();
+    try {
+      // Open Reader
+      System.out.println("Running Query:");
+      System.out.println(cmd.getSelect());
+      reader.open(cmd, conn);
+      // Print output
+      System.out.println("---------------------------------");
+      switch (queryType) {
+        case Reader:
+          // Text-Output by iterating through all records.
+          while (reader.moveNext()) {
+            System.out.println(reader.getString(EMP.EMPLOYEE_ID)
+                    + "\t" + reader.getString(EMPLOYEE_FULLNAME)
+                    + "\t" + EMP.GENDER.getOptions().get(reader.getString(EMP.GENDER))
+                    + "\t" + reader.getString(PHONE_EXT_NUMBER)
+                    + "\t" + reader.getString(DEP.NAME));
+          }
+          break;
+        case BeanList:
+          // Text-Output using a list of Java Beans supplied by the DBReader
+          List<SampleBean> beanList = reader.getBeanList(SampleBean.class);
+          System.out.println(String.valueOf(beanList.size()) + " SampleBeans returned from Query.");
+          for (SampleBean b : beanList) {
+            System.out.println(b.toString());
+          }
+          break;
+        case XmlDocument:
+          // XML Output
+          Document doc = reader.getXmlDocument();
+          // Print XML Document to System.out
+          XMLWriter.debug(doc);
+          break;
+      }
+
+    } finally {
+      // always close Reader
+      reader.close();
+    }
+  }
+
+  private void queryBeans(Connection conn) {
+    // Query all males
+    BeanResult<SampleBean> result = new BeanResult<SampleBean>(SampleBean.class, db.EMPLOYEES);
+    result.getCommand().where(db.EMPLOYEES.GENDER.is(Gender.M));
+    result.fetch(conn);
+
+    System.out.println("Number of male employees is: " + result.size());
+
+    // And now, the females
+    result.getCommand().where(db.EMPLOYEES.GENDER.is(Gender.F));
+    result.fetch(conn);
+
+    System.out.println("Number of female employees is: " + result.size());
+  }
+}
diff --git a/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleBean.java b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleBean.java
new file mode 100644
index 0000000..ac2b6cb
--- /dev/null
+++ b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleBean.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.empire.samples.springboot;
+
+/**
+ * The SampleBean class is used to demonstrate JavaBean support for SQL-Queries. The SampleBean is used in the SampleApp's queryRecords function.
+ */
+public class SampleBean {
+
+  private int employeeId;
+  private String fullName;
+  private String gender;
+  private String phoneNumber;
+  private String department;
+  private String businessUnit;
+
+  /*
+     * Uncomment this if you want to use constructor instead of setters
+     * However, number of arguments and data types must match query!
+     *
+    public SampleBean(int employeeId, String fullName, String gender, String phoneNumber, String department, String businessUnit)
+    {
+        this.employeeId = employeeId;
+        this.fullName = fullName;
+        this.gender = gender;
+        this.phoneNumber = phoneNumber;
+        this.department = department;
+        this.businessUnit = businessUnit;
+    }
+   */
+  public int getEmployeeId() {
+    return employeeId;
+  }
+
+  public void setEmployeeId(int employeeId) {
+    this.employeeId = employeeId;
+  }
+
+  public String getFullName() {
+    return fullName;
+  }
+
+  public void setFullName(String fullName) {
+    this.fullName = fullName;
+  }
+
+  public String getGender() {
+    return gender;
+  }
+
+  public void setGender(String gender) {
+    this.gender = gender;
+  }
+
+  public String getPhoneNumber() {
+    return phoneNumber;
+  }
+
+  public void setPhoneNumber(String phoneNumber) {
+    this.phoneNumber = phoneNumber;
+  }
+
+  public String getDepartment() {
+    return department;
+  }
+
+  public void setDepartment(String department) {
+    this.department = department;
+  }
+
+  public String getBusinessUnit() {
+    return businessUnit;
+  }
+
+  public void setBusinessUnit(String businessUnit) {
+    this.businessUnit = businessUnit;
+  }
+
+  @Override
+  public String toString() {
+    StringBuffer buf = new StringBuffer();
+    buf.append(employeeId);
+    buf.append("\t");
+    buf.append(fullName);
+    buf.append("\t");
+    buf.append(gender);
+    buf.append("\t");
+    buf.append(department);
+    buf.append("\t");
+    buf.append(businessUnit);
+    return buf.toString();
+  }
+
+}
diff --git a/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleConfig.java b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleConfig.java
new file mode 100644
index 0000000..f976c94
--- /dev/null
+++ b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleConfig.java
@@ -0,0 +1,8 @@
+package org.apache.empire.samples.springboot;
+
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class SampleConfig {
+
+}
diff --git a/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleDB.java b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleDB.java
new file mode 100644
index 0000000..d2329a3
--- /dev/null
+++ b/empire-db-examples/empire-db-example-spring-boot/src/main/java/org/apache/empire/samples/springboot/SampleDB.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.empire.samples.springboot;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBTable;
+import org.apache.empire.db.DBTableColumn;
+import org.springframework.stereotype.Component;
+
+/**
+ * <PRE>
+ * This file contains the definition of the data model in Java.
+ * The SampleDB data model consists of two tables and a foreign key relation.
+ * The tables are defined as nested classes here, but you may put them in separate files if you want to.
+ *
+ * PLEASE NOTE THE NAMING CONVENTION:
+ * Since all tables, views and columns are declared as "final" constants they are all in upper case.
+ * We recommend using a prefix of T_ for tables and C_ for columns in order to keep them together
+ * when listed in your IDE's code completion.
+ * There is no need to stick to this convention but it makes life just another little bit easier.
+ *
+ * You may declare other database tables or views in the same way.
+ * </PRE>
+ */
+@Component
+public class SampleDB extends DBDatabase
+{
+    private final static long serialVersionUID = 1L;
+    
+    public enum Gender
+    {
+    	M("Male"),
+    	F("Female"),
+        U("Unknown");
+        
+        private final String title;
+        private Gender(String title)
+        {
+            this.title = title;
+        }
+        @Override
+        public String toString()
+        {
+            return title;
+        }
+    }
+
+    /**
+     * This class represents the definition of the Departments table.
+     */
+    public static class Departments extends DBTable
+    {
+        private final static long serialVersionUID = 1L;
+      
+        public final DBTableColumn DEPARTMENT_ID;
+        public final DBTableColumn NAME;
+        public final DBTableColumn HEAD;
+        public final DBTableColumn BUSINESS_UNIT;
+        public final DBTableColumn UPDATE_TIMESTAMP;
+
+        public Departments(DBDatabase db)
+        {
+            super("DEPARTMENTS", db);
+            // ID
+            DEPARTMENT_ID   = addColumn("DEPARTMENT_ID",    DataType.AUTOINC,       0, true, "DEP_ID_SEQUENCE");
+            NAME            = addColumn("NAME",             DataType.VARCHAR,      80, true);
+            HEAD            = addColumn("HEAD",             DataType.VARCHAR,      80, false);
+            BUSINESS_UNIT   = addColumn("BUSINESS_UNIT",    DataType.VARCHAR,       4, true, "ITTK");
+            UPDATE_TIMESTAMP= addColumn("UPDATE_TIMESTAMP", DataType.TIMESTAMP,     0, true);
+
+            // Primary Key (automatically set due to AUTOINC column)
+            // setPrimaryKey(DEPARTMENT_ID);
+            // Set other Indexes
+            addIndex("DEPARTMENT_NAME_IDX", true, new DBColumn[] { NAME });
+        }
+    }
+
+    /**
+     * This class represents the definition of the Employees table.
+     */
+    public static class Employees extends DBTable
+    {
+        private final static long serialVersionUID = 1L;
+      
+        public final DBTableColumn EMPLOYEE_ID;
+        public final DBTableColumn SALUTATION;
+        public final DBTableColumn FIRSTNAME;
+        public final DBTableColumn LASTNAME;
+        public final DBTableColumn DATE_OF_BIRTH;
+        public final DBTableColumn DATE_WITH_TIME;
+        public final DBTableColumn DEPARTMENT_ID;
+        public final DBTableColumn GENDER;
+        public final DBTableColumn PHONE_NUMBER;
+        public final DBTableColumn EMAIL;
+        public final DBTableColumn SALARY;
+        public final DBTableColumn RETIRED;
+        public final DBTableColumn UPDATE_TIMESTAMP;
+
+        public Employees(DBDatabase db)
+        {
+            super("EMPLOYEES", db);
+            
+            // ID
+            EMPLOYEE_ID     = addColumn("EMPLOYEE_ID",      DataType.AUTOINC,      0, true, "EMPLOYEE_ID_SEQUENCE");
+            SALUTATION      = addColumn("SALUTATION",       DataType.VARCHAR,     20, false);
+            FIRSTNAME       = addColumn("FIRSTNAME",        DataType.VARCHAR,     40, true);
+            LASTNAME        = addColumn("LASTNAME",         DataType.VARCHAR,     40, true);
+            DATE_OF_BIRTH   = addColumn("DATE_OF_BIRTH",    DataType.DATE,         0, false);
+            DATE_WITH_TIME  = addColumn("DATE_WITH_TIME",   DataType.DATETIME,     0, false);
+            DEPARTMENT_ID   = addColumn("DEPARTMENT_ID",    DataType.INTEGER,      0, true);
+            GENDER          = addColumn("GENDER",           DataType.VARCHAR,      1, false, Gender.class);
+            PHONE_NUMBER    = addColumn("PHONE_NUMBER",     DataType.VARCHAR,     40, false);
+            EMAIL           = addColumn("EMAIL",            DataType.VARCHAR,     80, false);
+            SALARY          = addColumn("SALARY",           DataType.DECIMAL,   10.2, false);
+            RETIRED         = addColumn("RETIRED",          DataType.BOOL,         0, true, false);
+            UPDATE_TIMESTAMP= addColumn("UPDATE_TIMESTAMP", DataType.TIMESTAMP,    0, true);
+
+            // Primary Key (automatically set due to AUTOINC column)
+            // setPrimaryKey(EMPLOYEE_ID);
+            // Set other Indexes
+            addIndex("EMPLOYEE_NAME_IDX", true, new DBColumn[] { FIRSTNAME, LASTNAME, DATE_OF_BIRTH });
+        }
+    }
+
+    // Declare all Tables and Views here
+    public final Departments  DEPARTMENTS = new Departments(this);
+    public final Employees    EMPLOYEES   = new Employees(this);
+
+    /**
+     * Constructor of the SampleDB data model description
+     *
+     * Put all foreign key relations here.
+     */
+    public SampleDB()
+    {
+        // Define Foreign-Key Relations
+        addRelation( EMPLOYEES.DEPARTMENT_ID.referenceOn( DEPARTMENTS.DEPARTMENT_ID ));
+    }
+
+}
diff --git a/empire-db-examples/empire-db-example-spring-boot/src/main/resources/application.yml b/empire-db-examples/empire-db-example-spring-boot/src/main/resources/application.yml
new file mode 100644
index 0000000..ef268f8
--- /dev/null
+++ b/empire-db-examples/empire-db-example-spring-boot/src/main/resources/application.yml
@@ -0,0 +1,57 @@
+spring:
+  profiles:
+    active: hsqldb
+  datasource:
+    url: 'jdbc:hsqldb:file:hsqldb/sample;shutdown=true'
+    username: 'sa'
+    password: ''
+
+empiredb:
+  driverClass: 'org.apache.empire.db.hsql.DBDatabaseDriverHSql'
+
+---
+
+spring:
+  config:
+    activate:
+      on-profile: mysql
+
+empiredb:
+  driverClass: 'org.apache.empire.db.mysql.DBDatabaseDriverMySQL'
+  driverProperties:
+    databaseName: 'DBSAMPLE'
+
+---
+
+spring:
+  config:
+    activate:
+      on-profile: postgresql
+
+---
+
+spring:
+  config:
+    activate:
+      on-profile: h2
+
+---
+
+spring:
+  config:
+    activate:
+      on-profile: derby
+
+---
+
+spring:
+  config:
+    activate:
+      on-profile: oracle
+
+---
+
+spring:
+  config:
+    activate:
+      on-profile: sqlserver