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:
-
define a database schema that maps a single class to a single table
-
implement a primary key for each row of a table
-
define constraints for rows in a table
-
define an index for a table
-
define a DataSource to interface with the RDBMS
-
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.
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.
You can also get client access to using the following command.
|
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
|
1.1.3. Requirements
-
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.
-
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.propertiesspring.jpa.show-sql=true logging.level.org.hibernate.type=trace
-
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 insrc/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.propertiesspring.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. -
define the location for your schema migrations for flyway to automate.
spring.flyway.locations=classpath:db/migration/common,classpath:db/migration/{vendor}
-
-
Create a set of SQL migrations below
src/main/resources/db/migration
that will define the database schemaRefer 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. -
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-
define a sequence called
hibernate_sequence
-
define a HomeSale table with the necessary columns to store a flattened HomeSale object
-
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 -
define column constraints for size and not-null
-
-
account for when the table(s)/sequence(s) already exist by defining a DROP before creating
drop sequence IF EXISTS hibernate_sequence;
-
-
Create a separate SQL migration file to add indexes
-
define a non-unique index on home_id
-
define a non-unique index on buyer_id
-
-
Create a SQL migration file to add one row in the HomeSale table
CURRENT_DATE
can be used to generate a value forlist_date
andsale_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)
-
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.
-
-
Configure the application to establish a connection to the database and establish a DataSource
-
declare a dependency on
spring-boot-starter-data-jpa
-
declare a dependency on the
h2
database driver for default testing -
declare a dependency on the
postgresql
database driver for optional production-ready testing -
declare the database driver dependencies as
scope=runtime
See jpa-song-example
pom.xml for more details on declaring these dependencies.
-
-
Configure Flyway so that it automatically populates the database schema
-
declare a dependency on the
flyway-core
schema migration library -
declare the Flyway dependency as scope=runtime
See jpa-song-example
pom.xml for more details on declaring this plugin
-
-
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.
-
supply necessary
@SpringBootTest
test configurations unique to your environment -
supply an implementation of the
DbTestHelper
to be injected into all tests
-
-
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:
-
define a database schema that maps a single class to a single table
-
whether you have expressed your database schema in one or more files
-
-
implement a primary key for each row of a table
-
whether you have identified the primary key for the table
-
-
define constraints for rows in a table
-
whether you have defined size and nullable constrains for columns
-
-
define an index for a table
-
whether you have defined an index for any database columns
-
-
automate database schema migration with the Flyway tool
-
whether you have successfully populated the database schema from a set of files
-
-
define a DataSource to interface with the RDBMS
-
whether a DataSource was successfully injected into the JUnit class
-
1.1.5. Additional Details
-
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 tests2 assignment-tests
profile is activated for these service/DB-level tests only -
The following
starter
configuration files are used by the tests in this section:-
DbAssignmentTestConfiguration
- discussed above. Provides a@SpringBootConfiguration
class that removes the@SpringBootApplication
dependencies from view. -
DbClientTestConfiguration
- this defines the@Bean
factories for theDbTestHelper
and any supporting components. -
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:
-
define a PersistenceContext containing an
@Entity
class -
inject an EntityManager to perform actions on a Persistence Unit and database
-
map a simple
@Entity
class to the database using JPA mapping annotations -
perform basic database CRUD operations on an
@Entity
-
define transaction scopes
-
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.
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
-
Create a Business Object (BO)/
@Entity
class that represents the HomeSale and will be mapped to the database. ASaleBO
"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:-
identify the class as a JPA
@Entity
-
identify a String primary key field with JPA
@Id
-
supply a default constructor
-
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. -
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 thesrc/main
portion of your code. The JUnit test will make the condition and successful correction obvious.
-
-
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.-
map from BO to DTO
-
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) { ... }
-
-
Implement the
mapAndPersist
method in yourJpaAssignmentService
. It must perform the following:-
accept a HomeSale DTO
-
map the DTO to a HomeSale BO (using your mapper)
-
persist the BO
-
map the persisted BO to a DTO (will have a primary key assigned)
-
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. -
-
Implement the
queryByAgeRange
method in theJpaAssignmentService
using a@NamedQuery
. It must perform the following:-
query the database using a JPA
@NamedQuery
with JPA query syntax to locateHomeSale BO
objects within a saleAge min/max range, inclusiveYou 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
-
min
andmax
are variable integer values passed in at runtime -
order the results by
id
ascending -
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.
-
-
map the BO list returned from the query to a list of DTOs (using your mapper)
-
return the list of DTOs
-
-
Enable (and pass) the provided MyJpa5b_EntityTest that extends Jpa5b_EntityTest. This test will perform checks of the above functionality using:
-
DbTestHelper
-
mapper
-
your DTO and BO classes
-
a functional JPA environment
-
-
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:
-
inject an EntityManager to perform actions on a Persistence Unit and database
-
whether a EntityManager/EntityManager was successfully injected into the JUnit test
-
whether a EntityManager was successfully injected into your
JpaAssignmentService
implementation
-
-
map a simple
@Entity
class to the database using JPA mapping annotations-
whether a new HomeSale BO class was created for mapping to the database
-
-
implement a mapping tier between BO and DTO objects
-
whether the mapper was able to successfully map all fields between BO to DTO
-
whether the mapper was able to successfully map all fields between DTO to BO
-
-
perform basic database CRUD operations on an
@Entity
-
whether the HomeSale BO was successfully persisted to the database
-
whether a named JPA-QL query was used to locate the entity in the database
-
-
define transaction scopes
-
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:
-
declare a
JpaRepository
for an existing JPA@Entity
-
perform simple CRUD methods using provided repository methods
-
add paging and sorting to query methods
-
implement queries based on predicates derived from repository interface methods
-
implement queries based on POJO examples and configured matchers
-
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.
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
-
define a HomeSale JPARepository that can support basic CRUD and complete the queries defined below.
-
enable JpaRepository use with the
@EnableJpaRepositories
annotation on a@Configuration
classSpring 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 thesrc/main
portion of your code. The JUnit test will make the condition and successful correction obvious. -
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. -
implement the
findByHomeIdByDerivedQuery
method details which must-
accept a homeId and a Pageable specification with pageNumber, pageSize, and sort specification
-
return a Page of matching BOs that comply with the input criteria
-
this query must use the Spring Data Derived Query technique **
-
-
implement the
findByExample
method details which must-
accept a HomeSale BO probe instance and a Pageable specification with pageNumber, pageSize, and sort specification
-
return a Page of matching BOs that comply with the input criteria
-
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.
-
-
implement the
findByAgeRangeByAnnotatedQuery
method details which must-
accept a minimum and maximum age and a Pageable specification with pageNumber and pageSize
-
return a Page of matching BOs that comply with the input criteria and ordered by
id
-
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.
-
-
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. -
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:
-
declare a
JpaRepository
for an existing JPA@Entity
-
whether a
JPARepository
was defined and injected into the assignment service helper
-
-
perform simple CRUD methods using provided repository methods
-
whether the database was populated with test instances
-
-
add paging and sorting to query methods
-
whether the query methods where implemented with pageable specifications
-
-
implement queries based on predicates derived from repository interface methods
-
whether a derived query based on method signature was successfully performed
-
-
implement queries based on POJO examples and configured matchers
-
whether a query by example query was successfully performed
-
-
implement queries based on
@NamedQuery
or@Query
specification-
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:
-
declare project dependencies required for using Spring’s MongoOperations/MongoTemplate API
-
define a connection to a MongoDB
-
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.
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.
|
2.1.3. Requirements
-
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.propertieslogging.level.org.springframework.data.mongodb=DEBUG
-
provide a
mongodb
profile option to use an external MongoDB server instead of the Flapdoodle test instanceapplication-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 URLSpring 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 TestingFlapdoodle will be the default during testing unless deactivated by the presence of thespring.data.mongodb
connection properties.
-
-
Configure the application to establish a connection to the database and establish a MongoOperations (the interface)/MongoTemplate (the commonly referenced implementation class)
-
declare a dependency on
spring-boot-starter-data-mongo
-
declare a dependency on the
de.flapdoodle.embed.mongo
database driver for default testing withscope=test
See mongo-book-example
pom.xml for more details on declaring these dependencies.
-
-
Enable (and pass) the provided
MyMongo5a_ClientTest
that extendsMongo5a_ClientTest
. This test will verify connectivity to the database. -
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:
-
declare project dependencies required for using Spring’s MongoOperations/MongoTemplate API
-
whether required Maven dependencies where declared to operate and test the application with Mongo
-
-
define a connection to a MongoDB
-
whether a URL to the database was defined when the
mongodb
profile was activated
-
-
inject an MongoOperations/MongoTemplate instance to perform actions on a database
-
whether a MongoOperations client could be injected
-
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 tests2 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:
-
implement basic unit testing using an (seemingly) embedded MongoDB
-
define a
@Document
class to map to MongoDB collection -
perform whole-document CRUD operations on a
@Document
class using the Java API -
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.
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
-
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:
-
identify the class as a Spring Data Mongo
@Document
-
identify a String primary key field with Spring Data Mongo
@Id
This is a different @Id
annotation than the JPA@Id
annotation. -
supply a default constructor
-
-
Reuse the mapper class from the earlier JPA Entity portion of this assignment.
-
Implement the
mapAndPersist
method in yourMongoAssignmentService
. It must perform the following:-
accept a HomeSale DTO
-
map the DTO to a HomeSale BO (using your mapper)
-
persist the BO to the database
-
map the persisted BO to a DTO (will have a primary key assigned)
-
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.
-
-
Implement the
queryByAgeRange
method in yourMongoAssignmentService
. It must perform the following:-
query the database to locate matching
HomeSale BO
documents within a saleAge min/max range, inclusiveYou 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))
-
min and max are variable integer values passed in at runtime
-
order the results by
id
ascending
-
-
map the BO list returned from the query to a list of DTOs (using your mapper)
-
return the list of DTOs
-
-
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
-
-
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:
-
define a
@Document
class to map to MongoDB collection-
whether the BO class was properly mapped to the database, including document and primary key
-
-
perform whole-document CRUD operations on a
@Document
class using the Java API-
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:
-
declare a
MongoRepository
for an existing@Document
-
implement queries based on predicates derived from repository interface methods
-
implement queries based on POJO examples and configured matchers
-
implement queries based on annotations with JSON query expressions on interface methods
-
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.
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
-
define a HomeSale MongoRepository that can support basic CRUD and complete the queries defined below.
-
enable MongoRepository use with the
@EnableMongoRepositories
annotation on a@Configuration
classSpring 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 thesrc/main
portion of your code. The JUnit test will make the condition and successful correction obvious. -
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. -
implement the
findByHomeIdByDerivedQuery
method details which must-
accept a homeId and a Pageable specification with pageNumber, pageSize, and sort specification
-
return a Page of matching BOs that comply with the input criteria
-
this query must use the Spring Data Derived Query technique **
-
-
implement the
findByExample
method details which must-
accept a HomeSale BO probe instance and a Pageable specification with pageNumber, pageSize, and sort specification
-
return a Page of matching BOs that comply with the input criteria
-
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.
-
-
implement the
findByAgeRangeByAnnotatedQuery
method details which must-
accept a minimum and maximum age and a Pageable specification with pageNumber and pageSize
-
return a Page of matching BOs that comply with the input criteria and ordered by
id
-
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.
-
-
Enable (and pass) the provided
MyMongo5c_RepositoryTest
that extendsMongo5c_RepositoryTest
. This test will populate the database with content and issue query requests to yourMongoAssignmentService
implementation. -
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:
-
declare a
MongoRepository
for an existing@Document
-
whether a MongoRepository was declared and successfully integrated into the test case
-
-
implement queries based on predicates derived from repository interface methods
-
whether a dynamic query was implemented via the expression of the repository interface method name
-
-
implement queries based on POJO examples and configured matchers
-
whether a query was successfully implemented using an example with a probe document and matching rules
-
-
implement queries based on annotations with JSON query expressions on interface methods
-
whether a query was successfully implemented using annotated repository methods containing a JSON query and sort documents
-
-
add paging and sorting to query methods
-
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:
-
implement a service tier that completes useful actions
-
implement controller/service layer interactions relative to DTO and BO classes
-
determine the correct transaction propagation property for a service tier method
-
implement paging requests through the API
-
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.
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:
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
-
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. -
Update/Replace your legacy HomeSale Service and Repository components with a service and repository based on Spring Data Repository.
-
all CRUD calls will be handled by the Repository — no need for DataSource, EntityManager or MongoOperations/MongoTemplate
-
all queries must accept Pageable and return Page
By following the rule early in assignment 2, you made this transition extremely easy on yourself. -
the service should
-
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. -
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));
-
-
-
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 Approachpublic interface HomesPageableAPI extends HomesAPI { public static final String HOMES_PAGED_PATH = "/api/homes/paged";
There is a convenience method within
PageableDTO
(fromejava-dto-util
) that will convert pageNumber, pageSize, and sort to a Spring DataPageable
.@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();
-
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 (fromejava-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 (fromhomebuyers-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);
-
Add the capability to your API calls to provide and process the additional page information.
There is a convenience method within
PageableDTO
(fromejava-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 -
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. -
-
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:
-
implement a service tier that completes useful actions
-
whether you successfully implemented a query for HomeSales for a specific homeId
-
whether the service tier implemented the required query with Pageable inputs and a Page response
-
whether this was demonstrated thru a JUnit test
-
-
implement controller/service layer interactions when it comes to using DTO and BO classes
-
whether the controller worked exclusively with DTO business classes and implemented a thin API facade
-
whether the service layer mapped DTO and BO business classes and encapsulated the details of the service
-
-
determine the correct transaction propagation property for a service tier method
-
depending on the technology you select and the usecase you have implemented — whether the state of the database can ever reach an inconsistent state
-
-
implement paging requests through the API
-
whether your controller implemented a means to express Pageable request parameters for queries
-
-
implement page responses through the API
-
whether your controller supported returning a page-worth of results for query results
-
3.1.5. Additional Details
-
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
andSecurityConfiguration
classes are siblings of the same Java package and thebasePackageClasses
property of the database assignment is looking only for packages — not classes — and will accidentally bring in theHomeSalesSecurityApp
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 thebasePackageClasses
causes the entire package from that point down to be includedBy 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.
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
-
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. -
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. -
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.
-
Turn in a source tree with complete Maven modules that will build web application.
4.1.4. Grading
Your solution will be evaluated on:
-
whether you were able to implement persistence using a Spring Data Repository for Homes and Buyers.
-
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.