This assignment is broken up into three mandatory sections and an optional BONUS section for those that need extra credit.

The first two mandatory sections functionally work with Spring Data Repositories outside the scope of the HomeSales workflow. You will create a "service" class that is a peer to your HomeSales Service implementation — but this new class is there solely as a technology demonstration and wrapper for the provided JUnit tests. You will work with both JPA and Mongo Repositories as a part of these first two sections.

In the third mandatory section — you will select one of the two technologies, update the end-to-end thread with a Spring Data Repository, and add in some Pageable and Page aspects for unbounded collection query/results.

In the forth, optional BONUS section — you may switch technology selections and implement Homes or Buyers using a Spring Data Repository.

1. Assignment 5a: Spring Data JPA

1.1. Database Schema

1.1.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of preparing a relational database for use with an application. You will:

  1. define a database schema that maps a single class to a single table

  2. implement a primary key for each row of a table

  3. define constraints for rows in a table

  4. define an index for a table

  5. define a DataSource to interface with the RDBMS

  6. automate database schema migration with the Flyway tool

1.1.2. Overview

In this portion of the assignment you will be defining, instantiating, and performing minor population of a database schema for HomeSale. We will use a single, flat database design.

assignment5a homesales jpa schema
Figure 1. HomeSales Schema

I have shown the creation of a sequence despite choosing to use a varchar(36) primary key for the table. Please keep the sequence in your schema as sequences are commonly needed in RDBMS solutions.

Use char(36) to allow consistency with Mongo portion of assignment
Use the char-based primary key to make the JPA and Mongo portions of the assignment as similar as possible. We will use a UUID for the JPA portion, but any unique String fitting into 36 characters will work.
Postgres access with Docker/Docker Compose

If you have Docker/Docker Compose, you can instantiate a Postgres instance using the scripts in the ejava-springboot/env directory.

$ docker-compose up -d postgres
Creating network "ejava_default" with the default driver
Creating ejava_postgres_1 ... done

You can also get client access to using the following command.

$ docker-compose exec postgres psql -U postgres
psql (12.3)
Type "help" for help.

postgres=#

You can switch between in-memory H2 (default) and Postgres once you have your property files setup either by manual change of the source code or using runtime properties with the TestProfileResolver class provided in the starter module.

@SpringBootTest(...)
@ActiveProfiles(profiles={"assignment-tests","test"}, resolver = TestProfileResolver.class)
//@ActiveProfiles(profiles={"assignment-tests","test", "postgres"})
public class Jpa5a_SchemaTest {

1.1.3. Requirements

  1. Configure database properties so that you are able to work with both in-memory and external database. In-memory will be good for automated testing. Postgres will be good for interactive access to the database while developing.

    1. make the default database in-memory

      You can set the default database to h2 and activate the console by setting the following properties.

      application.properties
      #default test database
      spring.datasource.url=jdbc:h2:mem:homesales
      spring.h2.console.enabled=true

      You can turn on verbose JPA/SQL-related DEBUG logging using the following properties.

      application.properties
      spring.jpa.show-sql=true
      logging.level.org.hibernate.type=trace
    2. provide a "postgres" Spring profile option to use Postgres DB instead of in-memory

      You can switch to an alternate database by overriding the URL in a Spring profile. Add a postgres profile in src/main tree to optionally connect to an external Postgres server versus the in-memory H2 server. Include any necessary credentials. The following example assumes you will be connecting to the postgres DB launched by the class docker-compose.

      application-postgres.properties
      spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
      spring.datasource.username=postgres
      spring.datasource.password=secret
      This is only a class assignment. Do not store credentials in files checked into CM or packaged within your Spring Boot executable JAR in a real environment. Make them available via a file location at runtime when outside of a classroom.
    3. define the location for your schema migrations for flyway to automate.

      spring.flyway.locations=classpath:db/migration/common,classpath:db/migration/{vendor}
  2. Create a set of SQL migrations below src/main/resources/db/migration that will define the database schema

    Refer to the JPA songs example for a schema example. However, that example assumes that all schema is vendor-neutral and does not use vendor-specific sibling files.
    1. create SQL migration file(s) to define the base HomeSale schema. This can be hand-generated or metadata-generated once the @Entity class is later defined

      1. define a sequence called hibernate_sequence

      2. define a HomeSale table with the necessary columns to store a flattened HomeSale object

        1. use the id field as a primary key. Make this a char-based column type of at least 36 characters (varchar(36)) to host a UUID string

        2. define column constraints for size and not-null

      3. account for when the table(s)/sequence(s) already exist by defining a DROP before creating

        drop sequence IF EXISTS hibernate_sequence;
    2. Create a separate SQL migration file to add indexes

      1. define a non-unique index on home_id

      2. define a non-unique index on buyer_id

    3. Create a SQL migration file to add one row in the HomeSale table

      CURRENT_DATE can be used to generate a value for list_date and sale_date
      You may optionally arrange your population to mimic the lifecycle of a HomeSale by first inserting the initial listing and following up with a SQL update to later add the purchase information. This would allow you to test any not-null constraints against the expected lifecycle on a row.

      You can manually test schema files by launching the Postgres client and reading the SQL file in from stdin

      docker-compose exec -T postgres psql -U postgres < (path to file)
    4. Place vendor-neutral SQL in a common and vendor-specific SQL in a {vendor} directory as defined in your flyway properties. The example below shows a possible layout.

      src/main/resources/
      `-- db
          `-- migration
              |-- common
              |-- h2
              `-- postgres
      I am not anticipating any vendor-specific schema population, but it is a good practice if you use multiple database vendors between development and production.
  3. Configure the application to establish a connection to the database and establish a DataSource

    1. declare a dependency on spring-boot-starter-data-jpa

    2. declare a dependency on the h2 database driver for default testing

    3. declare a dependency on the postgresql database driver for optional production-ready testing

    4. declare the database driver dependencies as scope=runtime

      See jpa-song-example pom.xml for more details on declaring these dependencies.
  4. Configure Flyway so that it automatically populates the database schema

    1. declare a dependency on the flyway-core schema migration library

    2. declare the Flyway dependency as scope=runtime

      See jpa-song-example pom.xml for more details on declaring this plugin
  5. Enable (and pass) the provided MyJpa5a_SchemaTest that extends Jpa5a_SchemaTest. This test will verify connectivity to the database and the presence of the HomeSale table.

    1. supply necessary @SpringBootTest test configurations unique to your environment

    2. supply an implementation of the DbTestHelper to be injected into all tests

  6. Package the JUnit test case such that it executes with Maven as a surefire test

1.1.4. Grading

Your solution will be evaluated on:

  1. define a database schema that maps a single class to a single table

    1. whether you have expressed your database schema in one or more files

  2. implement a primary key for each row of a table

    1. whether you have identified the primary key for the table

  3. define constraints for rows in a table

    1. whether you have defined size and nullable constrains for columns

  4. define an index for a table

    1. whether you have defined an index for any database columns

  5. automate database schema migration with the Flyway tool

    1. whether you have successfully populated the database schema from a set of files

  6. define a DataSource to interface with the RDBMS

    1. whether a DataSource was successfully injected into the JUnit class

1.1.5. Additional Details

  1. This and the following RDBMS/JPA and MongoDB tests are all client-side DB interaction tests. Calls from JUnit are directed at the service class. The provided starter example supplies an alternate @SpringBootConfiguration test configuration to bypass the extra dependencies defined by the full @SpringBootApplication server class — which can cause conflicts. The @SpringBootConfiguration class is latched by the "assignment-tests" profile to keep it from being accidentally used by the later API tests.

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @Profile("assignment-tests") (1)
    public class DbAssignmentTestConfiguration {
    
    
    @SpringBootTest(classes={DbAssignmentTestConfiguration.class,
            JpaAssignmentDBConfiguration.class,
            DbClientTestConfiguration.class})
    @ActiveProfiles(profiles={"assignment-tests","test"}, resolver = TestProfileResolver.class)(2)
    //@ActiveProfiles(profiles={"assignment-tests","test", "postgres"})
    @Slf4j
    public class MyJpa5a_SchemaTest extends Jpa5a_SchemaTest {
    1 profile prevents @SpringBootConfiguration from being used as a @Configuration for other tests
    2 assignment-tests profile is activated for these service/DB-level tests only
  2. The following starter configuration files are used by the tests in this section:

    1. DbAssignmentTestConfiguration - discussed above. Provides a @SpringBootConfiguration class that removes the @SpringBootApplication dependencies from view.

    2. DbClientTestConfiguration - this defines the @Bean factories for the DbTestHelper and any supporting components.

    3. JpaAssignmentDBConfiguration - this defines server-side beans used in this DB-centric portion of the assignment. It provides @Bean factories that will get replaced when running the application and performing the end-to-end tests.

1.2. Entity/BO Class

1.2.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of defining a JPA @Entity class and performing basic CRUD actions. You will:

  1. define a PersistenceContext containing an @Entity class

  2. inject an EntityManager to perform actions on a Persistence Unit and database

  3. map a simple @Entity class to the database using JPA mapping annotations

  4. perform basic database CRUD operations on an @Entity

  5. define transaction scopes

  6. implement a mapping tier between BO and DTO objects

1.2.2. Overview

In this portion of the assignment you will be creating an @Entity/Business Object for a HomeSale, mapping that to a table, and performing CRUD actions with an EntityManager.

assignment5a homesales jpa2
Figure 2. HomeSale Entity

Your work will be focused in the following areas:

  • creating a business object (BO)/@Entity class to map to the database schema you have already completed

  • creating a mapper class that will map properties to/from the DTO and BO instances

  • creating a test helper class, implementing DbTestHelper that will assist the provided JUnit tests to interact and inspect your persistence implementation.

  • implementing a JpaAssignmentService component that will perform specific interactions with the database

The interfaces for the DbTestHelper and JpaAssignmentService are located in the support module containing the tests. The DbTestHelper interface extends the ApiTestHelper interface you have previously implemented and will simply extend with the additional functionality.

The BO and mapper classes will be used throughout this overall assignment, including the end-to-end. The testHelper class will be used for all provided JUnit tests. The JpaAssignmentService will only be used during the JPA-specific sections of this assignment. It is a sibling to your HomeSalesService component(s) for the purpose of doing one-off database assignment tasks. It will not be used in the Mongo portions of the assignment or the end-to-end.

1.2.3. Requirements

  1. Create a Business Object (BO)/@Entity class that represents the HomeSale and will be mapped to the database. A SaleBO "marker" interface has been provided for your BO class to implement. It has no properties. All interactions with this object by the JUnit test will be through calls to the testHelper and mapper classes. You must complete the following details:

    1. identify the class as a JPA @Entity

    2. identify a String primary key field with JPA @Id

    3. supply a default constructor

    4. supply other constructs as desired to help use and interact with this business object

      The BO class will map to a single, flat database row. Keep that in mind when accounting for the Home address. The properties/structure of the BO class do not have to be 1:1 with the properties/structure of the DTO class.
    5. supply a lifecycle event handler that will assign the string representation of a UUID to the id field if null when persisted

      @Entity @PrePersist Lifecycle Callback to assign Primary Key
          @PrePersist
          void prePersist() {
              if (id==null) {
                  id= UUID.randomUUID().toString();
              }
      If your Entity class is not within the default scan path, you can manually register the package path using the @EntityScan.basePackageClasses annotation property. This should be done within a @Configuration class in the src/main portion of your code. The JUnit test will make the condition and successful correction obvious.
  2. Create a mapper class that will map to/from HomeSale BO and DTO. A templated SalesMapper interface has been provided for this. You must complete the details.

    1. map from BO to DTO

    2. map from DTO to BO

      Remember — the structure of the BO and DTO classes do not have to match. Encapsulate any mapping details between the two within this mapper class implementation.

      The following code snippet shows an example implementation of the templated mapper interface.

      public class HomeSaleMapper implements SalesMapper<HomeSaleDTO, HomeSaleBO> {
          public HomeSaleBO map(HomeSaleDTO dto) { ... }
          public HomeSaleDTO map(HomeSaleBO bo) { ... }
  3. Implement the mapAndPersist method in your JpaAssignmentService. It must perform the following:

    1. accept a HomeSale DTO

    2. map the DTO to a HomeSale BO (using your mapper)

    3. persist the BO

    4. map the persisted BO to a DTO (will have a primary key assigned)

    5. return the resulting DTO

      The BO must be persisted. The returned DTO must match the input DTO and express the primary key assigned by the database.

    Be sure to address @Transactional details when modifying the database.
  4. Implement the queryByAgeRange method in the JpaAssignmentService using a @NamedQuery. It must perform the following:

    1. query the database using a JPA @NamedQuery with JPA query syntax to locate HomeSale BO objects within a saleAge min/max range, inclusive

      You may use the following query to start with and add ordering to complete

      select h from HomeSaleBO h where h.saleAge between :min and :max
      1. min and max are variable integer values passed in at runtime

      2. order the results by id ascending

      3. name the query "<EntityName>.findBySaleAgeRange"

        EntityName defaults to the Java SimpleName for the class. Make sure all uses of EntityName (i.e., JPAQL queries and JPA @NamedQuery name prefixes) match.
    2. map the BO list returned from the query to a list of DTOs (using your mapper)

    3. return the list of DTOs

  5. Enable (and pass) the provided MyJpa5b_EntityTest that extends Jpa5b_EntityTest. This test will perform checks of the above functionality using:

    1. DbTestHelper

    2. mapper

    3. your DTO and BO classes

    4. a functional JPA environment

  6. Package the JUnit test case such that it executes with Maven as a surefire test

1.2.4. Grading

Your solution will be evaluated on:

  1. inject an EntityManager to perform actions on a Persistence Unit and database

    1. whether a EntityManager/EntityManager was successfully injected into the JUnit test

    2. whether a EntityManager was successfully injected into your JpaAssignmentService implementation

  2. map a simple @Entity class to the database using JPA mapping annotations

    1. whether a new HomeSale BO class was created for mapping to the database

  3. implement a mapping tier between BO and DTO objects

    1. whether the mapper was able to successfully map all fields between BO to DTO

    2. whether the mapper was able to successfully map all fields between DTO to BO

  4. perform basic database CRUD operations on an @Entity

    1. whether the HomeSale BO was successfully persisted to the database

    2. whether a named JPA-QL query was used to locate the entity in the database

  5. define transaction scopes

    1. whether the test method was declared to use a single transaction for all steps of the test method

1.3. JPA Repository

1.3.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of defining a JPA Repository. You will:

  1. declare a JpaRepository for an existing JPA @Entity

  2. perform simple CRUD methods using provided repository methods

  3. add paging and sorting to query methods

  4. implement queries based on predicates derived from repository interface methods

  5. implement queries based on POJO examples and configured matchers

  6. implement queries based on @NamedQuery or @Query specification

1.3.2. Overview

In this portion of the assignment you will define a JPA Repository to perform basic CRUD and query actions.

assignment5a homesales repo
Figure 3. HomeSale Repository

Your work will be focused in the following areas:

  • creating a JPA Spring Data Repository for persisting HomeSale BO objects

  • implementing repository queries within your JpaAssignmentService component

1.3.3. Requirements

  1. define a HomeSale JPARepository that can support basic CRUD and complete the queries defined below.

  2. enable JpaRepository use with the @EnableJpaRepositories annotation on a @Configuration class

    Spring Data Repositories are primarily interfaces and the implementation is written for you at runtime using proxies and declarative configuration information.
    If your Repository class is not within the default scan path, you can manually register the package path using the @EnableJpaRepositories.basePackageClasses annotation property. This should be done within the src/main portion of your code. The JUnit test will make the condition and successful correction obvious.
  3. inject the JPA Repository class into your JpaAssignmentService component. This will be enough to tell you whether the Repository is properly defined and registered with the Spring context.

  4. implement the findByHomeIdByDerivedQuery method details which must

    1. accept a homeId and a Pageable specification with pageNumber, pageSize, and sort specification

    2. return a Page of matching BOs that comply with the input criteria

    3. this query must use the Spring Data Derived Query technique **

  5. implement the findByExample method details which must

    1. accept a HomeSale BO probe instance and a Pageable specification with pageNumber, pageSize, and sort specification

    2. return a Page of matching BOs that comply with the input criteria

    3. this query must use the Spring Data Query by Example technique **

      Override the default ExampleMatcher to ignore any fields declared with built-in data types that cannot be null.
  6. implement the findByAgeRangeByAnnotatedQuery method details which must

    1. accept a minimum and maximum age and a Pageable specification with pageNumber and pageSize

    2. return a Page of matching BOs that comply with the input criteria and ordered by id

    3. this query must use the Spring Data Named Query technique and leverage the "HomeSaleBO.findBySaleAgeRange" @NamedQuery created in the previous section.

      Named Queries do not support adding Sort criteria from the Pageable parameter. An "order by" for id must be expressed within the @NamedQuery.

      ... order by r.id ASC
      There is no technical relationship between the name of the service method you are implementing and the repository method defined on the JPA Spring Data Repository. The name of the service method is mangled to describe "how" you must implement it — not what the name of the repository method should be.
  7. Enable (and pass) the provided MyJpa5c_RepositoryTest that extends Jpa5c_RepositoryTest. This test will populate the database with content and issue query requests to your JpaAssignmentService implementation.

  8. Package the JUnit test case such that it executes with Maven as a surefire test

1.3.4. Grading

Your solution will be evaluated on:

  1. declare a JpaRepository for an existing JPA @Entity

    1. whether a JPARepository was defined and injected into the assignment service helper

  2. perform simple CRUD methods using provided repository methods

    1. whether the database was populated with test instances

  3. add paging and sorting to query methods

    1. whether the query methods where implemented with pageable specifications

  4. implement queries based on predicates derived from repository interface methods

    1. whether a derived query based on method signature was successfully performed

  5. implement queries based on POJO examples and configured matchers

    1. whether a query by example query was successfully performed

  6. implement queries based on @NamedQuery or @Query specification

    1. whether a query using a @NamedQuery or @Query source was successfully performed

2. Assignment 5b: Spring Data Mongo

2.1. Mongo Client Connection

2.1.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of setting up a project to work with Mongo. You will:

  1. declare project dependencies required for using Spring’s MongoOperations/MongoTemplate API

  2. define a connection to a MongoDB

  3. inject a MongoOperations/MongoTemplate instance to perform actions on a database

2.1.2. Overview

In this portion of the assignment you will be adding required dependencies and configuration properties necessary to communicate with the Flapdoodle test database and an external MongoDB instance.

assignment5b homesales mongo client
Figure 4. Mongo Client Connection
Postgres access with Docker/Docker Compose

If you have Docker/Docker Compose, you can instantiate a MongoDB instance using the docker-compose scripts in the ejava-springboot root directory.

$ docker-compose up -d mongodb
Creating ejava_mongodb_1 ... done

You can also get client access to using the following command.

$ docker-compose exec mongodb mongo -u admin -p secret
...
Welcome to the MongoDB shell.
>

You can switch between Flapdoodle and Mongo in your tests once you have your property files setup.

@SpringBootTest(...)
@ActiveProfiles(profiles={"assignment-tests","test"}, resolver = TestProfileResolver.class)
//@ActiveProfiles(profiles={"assignment-tests","test", "mongodb"})
public class MyMongo5a_ClientTest extends Mongo5a_ClientTest {

2.1.3. Requirements

  1. Configure database properties so that you are able to work with both the Flapdoodle test database and a Mongo instance. Flapdoodle will be good for automated testing. MongoDB will be good for interactive access to the database while developing. Spring Boot will automatically configure tests for Flapdoodle if it is in the classpath and in the absence of a Mongo database URI.

    You can turn on verbose MongoDB-related DEBUG logging using the following properties

    application.properties
    logging.level.org.springframework.data.mongodb=DEBUG
    1. provide a mongodb profile option to use an external MongoDB server instead of the Flapdoodle test instance

      application-mongodb.properties
      #spring.data.mongodb.host=localhost
      #spring.data.mongodb.port=27017
      #spring.data.mongodb.database=test
      #spring.data.mongodb.authentication-database=admin
      #spring.data.mongodb.username=admin
      #spring.data.mongodb.password=secret
      spring.data.mongodb.uri=mongodb://admin:secret@localhost:27017/test?authSource=admin
      Configure via Individual Properties or Compound URL
      Spring Data Mongo has the capability to set individual configuration properties or via one, compound URL.
      This is only a class assignment. Do not store credentials in files checked into CM or packaged within your Spring Boot executable JAR in a real environment. Make them available via a file location at runtime when outside of a classroom.
      Flapdoodle will be Default Database during Testing
      Flapdoodle will be the default during testing unless deactivated by the presence of the spring.data.mongodb connection properties.
  2. Configure the application to establish a connection to the database and establish a MongoOperations (the interface)/MongoTemplate (the commonly referenced implementation class)

    1. declare a dependency on spring-boot-starter-data-mongo

    2. declare a dependency on the de.flapdoodle.embed.mongo database driver for default testing with scope=test

      See mongo-book-example pom.xml for more details on declaring these dependencies.
  3. Enable (and pass) the provided MyMongo5a_ClientTest that extends Mongo5a_ClientTest. This test will verify connectivity to the database.

  4. Package the JUnit test case such that it executes with Maven as a surefire test

2.1.4. Grading

Your solution will be evaluated on:

  1. declare project dependencies required for using Spring’s MongoOperations/MongoTemplate API

    1. whether required Maven dependencies where declared to operate and test the application with Mongo

  2. define a connection to a MongoDB

    1. whether a URL to the database was defined when the mongodb profile was activated

  3. inject an MongoOperations/MongoTemplate instance to perform actions on a database

    1. whether a MongoOperations client could be injected

    2. whether the MongoOperations client could successfully communicate with the database

2.1.5. Additional Details

  • As with the RDBMS/JPA tests, these MongoDB tests are all client-side DB interaction tests. Calls from JUnit are directed at the service class. The provided starter example supplies an alternate @SpringBootConfiguration test configuration to bypass the extra dependencies defined by the full @SpringBootApplication server class — which can cause conflicts. The @SpringBootConfiguration class is latched by the "assignment-tests" profile to keep it from being accidentally used by the later API tests.

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @Profile("assignment-tests") (1)
    public class DbAssignmentTestConfiguration {
    
    @SpringBootTest(classes={DbAssignmentTestConfiguration.class,
            MongoAssignmentDBConfiguration.class,
            DbClientTestConfiguration.class
    })
    @ActiveProfiles(profiles={"assignment-tests","test"}, resolver = TestProfileResolver.class)(2)
    //@ActiveProfiles(profiles={"assignment-tests","test", "mongodb"})
    public class MyMongo5a_ClientTest extends Mongo5a_ClientTest {
    1 profile prevents @SpringBootConfiguration from being used as a @Configuration for other tests
    2 assignment-tests profile is activated for these service/DB-level tests only

2.2. Mongo Document

2.2.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of defining a Spring Data Mongo @Document class and performing basic CRUD actions. You will:

  1. implement basic unit testing using an (seemingly) embedded MongoDB

  2. define a @Document class to map to MongoDB collection

  3. perform whole-document CRUD operations on a @Document class using the Java API

  4. perform queries with paging properties

2.2.2. Overview

In this portion of the assignment you will be creating a @Document/Business Object for a HomeSale, mapping that to a collection, and performing CRUD actions with a MongoOperations/MongoTemplate.

assignment5b homesales mongo doc
Figure 5. HomeSale Entity
Reuse BO and Mapper classes
It has been designed and expected that you will be able to re-use the same HomeSale BO and Mapper classes from the JPA portion of the assignment. You should not need to create new ones. The BO class will need a few Spring Data Mongo annotations but the mapper created for the JPA portion should be 100% reusable here as well.

2.2.3. Requirements

  1. Create (reuse) a Business Object (BO) class that represents the HomeSale and will be mapped to the database. A SaleBO "marker" interface has been provided for your BO class to implement. It has no properties. All interactions with this object by the JUnit test will be through calls to the testHelper and mapper classes. You must complete the following details:

    1. identify the class as a Spring Data Mongo @Document

    2. identify a String primary key field with Spring Data Mongo @Id

      This is a different @Id annotation than the JPA @Id annotation.
    3. supply a default constructor

  2. Reuse the mapper class from the earlier JPA Entity portion of this assignment.

  3. Implement the mapAndPersist method in your MongoAssignmentService. It must perform the following:

    1. accept a HomeSale DTO

    2. map the DTO to a HomeSale BO (using your mapper)

    3. persist the BO to the database

    4. map the persisted BO to a DTO (will have a primary key assigned)

    5. return the resulting DTO

      The BO must be persisted. The returned DTO must match the input DTO and express the primary key assigned by the database.

  4. Implement the queryByAgeRange method in your MongoAssignmentService. It must perform the following:

    1. query the database to locate matching HomeSale BO documents within a saleAge min/max range, inclusive

      You may use the injected MongoOperations client find command, a query, and the HomeSaleBO.class as a request parameter

      You may make use of the following query

      Query.query(Criteria.where("saleAge").gte(min).lte(max))
      1. min and max are variable integer values passed in at runtime

      2. order the results by id ascending

    2. map the BO list returned from the query to a list of DTOs (using your mapper)

    3. return the list of DTOs

  5. Enable (and pass) the provided MyMongo5b_DocumentTest that extends Mongo5b_DocumentTest. This test will perform checks of the above functionality using:

    • DbTestHelper

    • mapper

    • your DTO and BO classes

    • a functional MongoDB environment

  6. Package the JUnit test case such that it executes with Maven as a surefire test

2.2.4. Grading

Your solution will be evaluated on:

  1. define a @Document class to map to MongoDB collection

    1. whether the BO class was properly mapped to the database, including document and primary key

  2. perform whole-document CRUD operations on a @Document class using the Java API

    1. whether a successful insert and query of the database was performed with the injected MongoOperations / MongoTemplate

2.3. Mongo Repository

2.3.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of defining a Mongo Repository. You will:

  1. declare a MongoRepository for an existing @Document

  2. implement queries based on predicates derived from repository interface methods

  3. implement queries based on POJO examples and configured matchers

  4. implement queries based on annotations with JSON query expressions on interface methods

  5. add paging and sorting to query methods

2.3.2. Overview

In this portion of the assignment you will define a Mongo Repository to perform basic CRUD and query actions.

assignment5b homesales mongo repo
Figure 6. Mongo Repository

Your work will be focused in the following areas:

  • creating a Mongo Spring Data Repository for persisting HomeSale BO objects

  • implementing repository queries within your MongoAssignmentService component

2.3.3. Requirements

  1. define a HomeSale MongoRepository that can support basic CRUD and complete the queries defined below.

  2. enable MongoRepository use with the @EnableMongoRepositories annotation on a @Configuration class

    Spring Data Repositories are primarily interfaces and the implementation is written for you using proxies and declarative configuration information.
    If your Repository class is not within the default scan path, you can manually register the package path using the @EnableMongoRepositories.basePackageClasses annotation property. This should be done within a @Configuration class in the src/main portion of your code. The JUnit test will make the condition and successful correction obvious.
  3. inject the Mongo Repository class into the MongoAssignmentService. This will be enough to tell you whether the Repository is properly defined and registered with the Spring context.

  4. implement the findByHomeIdByDerivedQuery method details which must

    1. accept a homeId and a Pageable specification with pageNumber, pageSize, and sort specification

    2. return a Page of matching BOs that comply with the input criteria

    3. this query must use the Spring Data Derived Query technique **

  5. implement the findByExample method details which must

    1. accept a HomeSale BO probe instance and a Pageable specification with pageNumber, pageSize, and sort specification

    2. return a Page of matching BOs that comply with the input criteria

    3. this query must use the Spring Data Query by Example technique **

      Override the default ExampleMatcher to ignore any fields declared with built-in data types that cannot be null.
  6. implement the findByAgeRangeByAnnotatedQuery method details which must

    1. accept a minimum and maximum age and a Pageable specification with pageNumber and pageSize

    2. return a Page of matching BOs that comply with the input criteria and ordered by id

    3. this query must use the Spring Data JSON-based Query Methods technique and annotate the repository method with a @Query definition.

      You may use the following JSON query expression for this query. Mongo JSON query expressions only support positional arguments and are zero-relative.

      value="{ saleAge : {$gte:?0, $lte: ?1} }" (1)
      1 min is position 0 and max is position 1 in the method signature

      Annotated Queries do not support adding Sort criteria from the Pageable parameter. You may use the following sort expression in the annotation

      sort="{id:1}"
      There is no technical relationship between the name of the service method you are implementing and the repository method defined on the Mongo Spring Data Repository. The name of the service method is mangled to describe "how" you must implement it — not what the name of the repository method should be.
  7. Enable (and pass) the provided MyMongo5c_RepositoryTest that extends Mongo5c_RepositoryTest. This test will populate the database with content and issue query requests to your MongoAssignmentService implementation.

  8. Package the JUnit test case such that it executes with Maven as a surefire test

2.3.4. Grading

Your solution will be evaluated on:

  1. declare a MongoRepository for an existing @Document

    1. whether a MongoRepository was declared and successfully integrated into the test case

  2. implement queries based on predicates derived from repository interface methods

    1. whether a dynamic query was implemented via the expression of the repository interface method name

  3. implement queries based on POJO examples and configured matchers

    1. whether a query was successfully implemented using an example with a probe document and matching rules

  4. implement queries based on annotations with JSON query expressions on interface methods

    1. whether a query was successfully implemented using annotated repository methods containing a JSON query and sort documents

  5. add paging and sorting to query methods

    1. whether queries were performed with sorting and paging

3. Assignment 5c: Spring Data Application

3.1. API/Service/DB End-to-End

3.1.1. Purpose

In this portion of the assignment, you will demonstrate your knowledge of integrating a Spring Data Repository into an end-to-end application, accessed through an API. You will:

  1. implement a service tier that completes useful actions

  2. implement controller/service layer interactions relative to DTO and BO classes

  3. determine the correct transaction propagation property for a service tier method

  4. implement paging requests through the API

  5. implement page responses through the API

3.1.2. Overview

In this portion of the assignment you will be taking elements of the application that you have worked on either together or independently and integrate them into an end-to-end application from the API, thru services, security, the repository, and to the database and back.

assignment5a homesales jpa4
Figure 7. API/Service/DB End-to-End

The fundamental scope of the assignment is to perform existing HomeSales use cases (including security layer(s)) but updated with database and the impacts of database interaction related to eventual size and scale. You will

  • chose either a RDBMS/JPA or Mongo target solution

  • replace your existing HomeSale Service and Repository implementation classes with implementations that are based upon your selected repository technology

    For those that

    • augmented your assignment2/API solution in-place to meet the requirements of each follow-on assignment — you will continue that pattern by replacing the core service logic with mapper/BO/repository logic.

    • layered your solution along assignment boundaries, you will override the assignment2 service and repository components with a new service implementation based on the mapper/BO/repository logic. The support modules show an example of doing this.

  • update the controller and service interfaces to address paging

    It is again, your option of whether to

    • simply add the new paging endpoint to your existing controller and API client class

    • subclass the controller and API class to add the functionality

    The Homes/Buyers support modules are provided in a layered approach to help identify what is new with each level and to keep what you are basing your solutions on consistent. It is much harder to implement the layered approach, but offers some challenges and experience in integrating multiple components.

  • leave the Homes and Buyers implementation as in-memory repositories

There are two optional support modules supplied:

  • homebuyers-support-svcjpa provides an implementation of Homes using JpaRepository

  • homebuyers-support-svcmongo provides an implementation of Buyers using MongoRepository

The tests within each module work but extensive testing with HomeSales has not been performed. It is anticipated that you will continue to use the in-memory Homes and Buyers that you have been using to date. However, it is your option to use those modules in any way.

Continued use of in-memory Homes and Buyers is expected
The homebuyers-support-svcjpa and homebuyers-support-svcmongo modules are provided as examples of how the flow can be implemented. It is not a requirement that you change from the in-memory versions to complete this assignment.

3.1.3. Requirements

  1. Select a database implementation choice (JpaRepository or MongoRepository).

    This is a choice to move forward with. The option you don’t select will still be part of your dependencies, source tree, and completed unit integration tests.
  2. Update/Replace your legacy HomeSale Service and Repository components with a service and repository based on Spring Data Repository.

    1. all CRUD calls will be handled by the Repository — no need for DataSource, EntityManager or MongoOperations/MongoTemplate

    2. all queries must accept Pageable and return Page

      By following the rule early in assignment 2, you made this transition extremely easy on yourself.
    3. the service should

      1. accept DTO types as input and map to BOs for interaction with the Repository

        This is should be consistent with your original service interface. The only change should be the conversion of DTO to BO and back.
      2. map BO types to DTOs as output

        This should also be consistent with your original service interface. The Page class has a nice/easy way to map between Page<T1> to Page<T2>. When you combine that with your mapper — it can be a simple one-line of code.

        dtos = bos.map(bo->map(bo));
  3. Add the capability to your controller to accept full paging parameters. For those augmenting in-place, you may simply modify your existing finder methods to accept/return the additional information. For those adopting the layered approach, you may add an additional URI to accept/return the additional information.

    Example new Paged URI for Layered Approach
    public interface HomesPageableAPI extends HomesAPI {
        public static final String HOMES_PAGED_PATH = "/api/homes/paged";

    There is a convenience method within PageableDTO (from ejava-dto-util) that will convert pageNumber, pageSize, and sort to a Spring Data Pageable.

    @GetMapping(path = HomesPageableAPI.HOMES_PAGED_PATH, ...)
    public ResponseEntity<HomePageDTO> getHomesPage(
        @RequestParam(value = "pageNumber", required = false) Integer pageNumber,
        @RequestParam(value = "pageSize", required = false) Integer pageSize,
        @RequestParam(value = "sort", required = false) String sort) {
      Pageable pageable = PageableDTO.of(pageNumber, pageSize, sort).toPageable();
  4. Add the capability to your controller to return paging information with the contents. The current pageNumber, pageSize, and sort that relate to the supplied data must be returned with the contents.

    You may use the Page<T> class (from ejava-dto-util) to automate/encapsulate much of this. The primary requirement is to convey the information. The option is yours of whether to use this library demonstrated in the class JPA and Mongo examples as well as the Homes and Buyers examples (from homebuyers-support-pageable-svc)

    public class HomePageDTO extends PageDTO<HomeDTO> {
        protected HomePageDTO() {}
        public HomePageDTO(Page<HomeDTO> page) {
            super(page);
        }
    }
    Page<HomeDTO> result = service.getHomes(pageable);
    HomePageDTO resultDTO = new HomePageDTO(result);
    return ResponseEntity.ok(resultDTO);
  5. Add the capability to your API calls to provide and process the additional page information.

    There is a convenience method within PageableDTO (from ejava-dto-util) that will serialize the pageNumber, pageSize, and sort of Spring Data’s Pageable into query parameters.

    PageableDTO pageableDTO = PageableDTO.fromPageable(pageable); (1)
    URI url = UriComponentsBuilder
            .fromUri(homesUrl)
            .queryParams(pageableDTO.getQueryParams()) (2)
            .build().toUri();
    1 create DTO abstraction from Spring Data’s local Pageable abstraction
    2 transfer DTO representation into query parameters
  6. Write a JUnit Integration Test Case that will

    • populate the database with multiple HomeSales with different and similar properties

    • query for HomeSales based on a criteria that will match some of the HomeSales and return a page of contents that is less than the total matches in the database. (i.e., make the pageSize smaller that total number of matches)

    • page through the results until the end of data is encountered

      Again — the DTO Paging framework in common and the JPA Songs and Mongo Books examples should make this less heroic than it may sound.
    The requirement is not that you integrate with the provided DTO Paging framework. The requirement is that you implement end-to-end paging and the provided framework can take a lot of the API burden off of you. You may implement page specification and page results in a unique manner as long as it is end-to-end.
  7. Package the JUnit test case such that it executes with Maven as a surefire test

3.1.4. Grading

Your solution will be evaluated on:

  1. implement a service tier that completes useful actions

    1. whether you successfully implemented a query for HomeSales for a specific homeId

    2. whether the service tier implemented the required query with Pageable inputs and a Page response

    3. whether this was demonstrated thru a JUnit test

  2. implement controller/service layer interactions when it comes to using DTO and BO classes

    1. whether the controller worked exclusively with DTO business classes and implemented a thin API facade

    2. whether the service layer mapped DTO and BO business classes and encapsulated the details of the service

  3. determine the correct transaction propagation property for a service tier method

    1. depending on the technology you select and the usecase you have implemented — whether the state of the database can ever reach an inconsistent state

  4. implement paging requests through the API

    1. whether your controller implemented a means to express Pageable request parameters for queries

  5. implement page responses through the API

    1. whether your controller supported returning a page-worth of results for query results

3.1.5. Additional Details

  1. This is a bit in the weeds, but something that came up following the layered approach, not paying attention to component scan paths when placing classes into Java packages, and not wanting to change code that you have built upon. The provided assignment3 @SpringBootApplication and SecurityConfiguration classes are siblings of the same Java package and the basePackageClasses property of the database assignment is looking only for packages — not classes — and will accidentally bring in the HomeSalesSecurityApp class with a normal reference.

    src/main/java/info/ejava_student/starter/assignment3/
    |-- aop
    `-- security
        |-- HomeSalesSecurityApp.java
        |-- SecurityConfiguration.java (1)
        |-- homesales
        `-- identity
    1 naming SecurityConfiguration in the basePackageClasses causes the entire package from that point down to be included

    By using @ComponentScan, we can define the scan path in more detail and supply a list of filters for what to exclude.

    Excluding Classes from Scan Path
    @SpringBootApplication
    @ComponentScan(
        basePackageClasses={
                ...
            SecurityConfiguration.class, //SecurityFilterChain
                ...
        },
        excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
                    HomeSalesSecurityApp.class
        })
    })

4. Assignment 5d: Bonus

If you feel you got a slow start to the semester and are now finally getting on a role, the following is an optional BONUS assignment for your consideration. If you complete this bonus assignment successfully and need a ~half-grade boost to get you into the next tier you may want to read on.

4.1. Homes/Buyers Alternate Repository

4.1.1. Purpose

In this portion of the optional BONUS assignment, you will further demonstrate your knowledge of integrating a Spring Data Repository into an end-to-end application, accessed through an API of the opposite technology than you chose for HomeSales.

You will also demonstrate the ability to weave a new component implementation into the Spring context to replace an existing in-memory Repository.

4.1.2. Homes/Buyers Alternate Repository

In this portion of the optional BONUS assignment you will be replacing the in-memory Homes and Buyers Services/Repositories with a repository-backed implementation.

You may use the same technology choice from your end-to-end solution to map all three applications.

You will do so for HomeSales as well as Homes and Buyers and include security and functional capabilities.

assignment5d homesales bonus a
Figure 8. API/Service/DB End-to-End

You went through the JPA and Mongo setup as part of your Spring Data Assignment. There are further JPA and Mongo end-to-end examples in the homebuyers-support-db-svcjpa and homebuyers-support-db-svcmongo support modules that can be leveraged.

Use what you can out of homebuyers-support-db-svcjpa and homebuyers-support-db-svcmongo modules, but when mapping to the alternate database type — you will clearly need to fully replace that portion of the code. It is expected that you will be able to fully use the security and controller support layers and then insert your mapping of the Homes or Buyers to the database.

4.1.3. Requirements

  1. Implement a repository and service class mapping Homes to the database of choice. You will need to implement the HomeBO class and mapping between the DTO and BO type.

    Portions of the JPA thread for Homes is provided in the homebuyers-support-db-svcjpa example.
  2. Implement a repository and service class mapping Buyers to the database of choice. You will need to implement the BuyerBO class and mapping between the DTO and BO type.

    Portions of the Mongo thread for Buyers is provided in the homebuyers-support-db-svcmongo example.
  3. Provided a JUnit unit integration test that will demonstrate a successful registration — from Home/Buyer creation to HomeSale completion, using your database mappings for all services.

  4. Turn in a source tree with complete Maven modules that will build web application.

4.1.4. Grading

Your solution will be evaluated on:

  1. whether you were able to implement persistence using a Spring Data Repository for Homes and Buyers.

  2. whether you were able to integrate the database repositories for Homes and Buyers into the service and security logic for Homes and Buyers as part of the end-to-end scenario.