Spring mongodb how to use crudRespository with embedded documents - schema

Why when I created crudRespository to do search on embedded object do none of the queries work and all of them return null. Is there something I'm doing wrong? There is an example.
I hope this is not a limitation of the spring mongodb. Maybe you know a different way to extract embedded object? I could use #DbRef but I read that it is not recommended for the performance issues.
Country.class
#Document(collection="countries")
#TypeAlias("ctry")
public class Country {
#Id private BigInteger id;
private String name;
private Continent continent;
public Country(String name, Continent continent) {
setName(name);
setContinent(continent);
}
//get/set omitted
}
Embedded document Continent.class
public final class Continent {
private final long id;
private final String name;
public Continent(long id, String name) {
this.id = id;
this.name = name;
}
//get/set omitted
}
ContinentRespository.class - this works fine the data is returned as it should
public interface CountryRepository extends Repository<Country, BigInteger> {
//------------------------------------------- equality
public Country findByName(String countryName);
#Query("{name : ?0}")
public Country findByNameQuery(String countryName);
}
ContinentRespository.class - all the methods always return null :/
public interface ContinentRepository extends CrudRepository<Continent, Long> {
public Continent findByName(String name);
#Query("{name : ?0}")
public Continent findByNameQuery(String name);
}
RespositoryTest.class - test class testFindEmbeddedContinent fails because returned object is null. testFindDocumentCountry works as expected
#ContextConfiguration(classes={MongoConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class RepositoryTest {
#Autowired private CountryRepository countryRepository;
#Autowired private ContinentRepository continentRepo;
#Autowired private MongoOperations mongoOps;
#Autowired private MongoTemplate template;
#Before
public void reset() {
DBCollection countries = template.getCollection("countries");
countries.drop();
countries.insert(dbObjectFromJson("{ '_id' : '8', '_class' : 'ctry', 'name' : 'Japan', 'continent' : { '_id' : 2, 'name' : 'Asia' } }"));
}
#Test
public void testFindEmbeddedContinent() {
Continent asia = continentRepo.findByName("Asia");
assertNotNull(asia);
assertThat(asia.getName(), is(equalTo("Asia")));
}
#Test
public void testFindDocumentCountry() {
Country japan = countryRepository.findByName("Japan");
assertNotNull(japan);
assertThat(japan.getContinent().getName(), is(equalTo("Asia")));
assertThat(japan.getName(), is(equalTo("Japan")));
}
private static DBObject dbObjectFromJson(String json) {
return (DBObject) JSON.parse(json);
}
}

A repository - by definition - mimics a collection of aggregate roots. In MongoDB this translates into top-level documents of a collection. Thus, you can only create repositories for top-level documents, i.e. Country in your case.
Im an not totally sure that it makes sense that a Continent is a property of a Country (as usually a country is part of a continent) but that might just be due to the sample. If you want to query Continents independently, make them a top-level document and keep references to the country either using simple ID or a DBRef (the former recommended).

Related

Using inheritance with a parent class which contains empty strings for variables that are only applicable in child classes

I am trying to model geographic locations in OOP. Location types are as follows:continent, country, state, county or more specific (e.g city, town, village, all modeled as a single type). Continents have continent code, counties have continent code and country code, states have continent code, country code and adm1code, counties and those more specific than counties have continent code, country code, adm1code and adm2code. I need all locations to implement an interface with method "isContainedIn(Location loc)". This checks whether the current location is contained in loc based on the administrative code. So in case of countries, this method would first check if loc is Continent - if not, return false. If it is continent, then it will check whether the continent code of loc is the same as the continent code of the country.
I can model the locations using a single class with a type field or use inheritance. I would rather model using inheritance with a base class called location representing locations more specific than adm2 which all administrative location types (country, code, state and county) would extend (which conceptually is not right - I'm saying that town is the parent class of continent). This way I can simply override other methods too such as the equals operator (e.g. two countries are equal if the have same country codes). However, then the base class would need to include continent code, country code, adm1code and adm2code to allow me to implement the isContainedIn(Location loc) method in every case. Country code does not make sense for continents, adm1code does not make sense for countries and so on. I can have blank strings when they don't make sense but would that violate Liskov substitution principle or any other standard design principle? If so, could you suggest other design options for this problem?
EDIT: I would want a client class to get two location instances, say l1 and l2, and be able to call l1.isContainedIn(l2) without knowing the specific type of the location. Let me elaborate on how it becomes a problem if I model the classes as follows:
1. A parent class containing only the variables making sense for all locations which are name, latitude and longitude
2. Children classes (continent, country, state, county and more specific), all implementing the isContainedIn(Location loc) interface. Continent would have continent code, country would have continent code & country code and so on.
With this structure I simply can't write out the logic for isContainedIn(Location loc) in any of the child classes. For instance, a location is contained in a continent if 1) it is not a continent and 2) it has the same continent code as the continent. But Location class has no continent code. I hope this clarifies the question and thanks a lot for looking into this!
EDIT2: Here is some example code (instead of interface I have an abstract class):
abstract class Location {
protected String name;
protected double lat;
protected double lng;
#Override
public boolean equals(Object loc) {
if(!(loc instanceof Location)) {
return false;
}
Location l = (Location) loc;
return this.lat==l.lat && this.lng==l.lng;
}
abstract boolean isContainedIn(Location loc);
}
class Continent extends Location {
protected String continentCode;
#Override
public boolean equals(Object loc) {
if(!(loc instanceof Continent)) {
return false;
}
Location l = (Continent) loc;
return this.continentCode.equals(loc.continentCode);
}
boolean isContainedIn(Location loc) {
if(loc instance of Continent) {
return false;
}
//the following is the logic but won't work since location has no
continentCode variable
//return loc.continentCode.equals(continentCode);
}
}
class Country extends Location {
protected String continentCode;
protected String countryCode;
#Override
public boolean equals(Object loc) {
if(!(loc instanceof Country)) {
return false;
}
Location l = (Country) loc;
return this.continentCode.equals(loc.continentCode) && this.countryCode.equals(loc.countryCode);
}
boolean isContainedIn(Location loc) {
if(loc instance of Continent|| loc instance of Country) {
return false;
}
//the following is the logic but won't work since location has no
countryCode or continent code variable
//return loc.continentCode.equals(continentCode) && loc.countryCode.equals(countryCode);
}
}
//other classes will be similar
This is what the client class might look like
class ClientClass {
void someMethod {
Location l1 = someClass.getLocation(String...searchParameters);
Location l2 = someClass.getLocation(String...searchParameters);
if(l1.isContainedIn(l2)) {
//do something
}
}
Objective: Model the location classes, to enable the client code to use isContainedIn method without knowing the specific type of the location. Can this be done if parent class has no knowledge of continent code, country code etc?
Alternate class design
//for locations contained in counties
class Location {
String name;
double lat;
double lng;
String continentCode;
String countryCode;
String adm1code;
String adm2code;
}
class Continent extends Location {
String countryCode ="";
String adm1code = "";
String adm2code = "";
public Continent(String continentCode) {
this.continentCode = continentCode;
}
//all logic will work but does that violate design principles since Continent technically has no country code, adm1code or adm2code - blank strings returned for these cases?
Thanks.
You've fallen into the classic OOP trap of thinking that just because two things represent the same concept, here a location on the Earth's surface, that they should share a base class.
At the start of the question, you state you want them "all modeled as a single type". The single type you've chosen, a single point Location doesn't make sense and leads you to ask in code: location1.contains(location2). How can a single point contain another? Also, you don't use the lat/long to make the decision whether a location contains another, so it's a red herring for the question. If you want to add location for another reason, it should be a property of an item, not a base class.
So, I know it goes against your whole premise, but I'm going to propose another solution.
interface ContinentEntity {
Continent getContinent();
}
// Logically anything that is of a country is also of a continent
interface CountryEntity extends ContinentEntity {
Country getCountry();
// We can satisfy this here in Java 8
default Continent getContinent() {
return getCountry().getContinent();
}
}
public final class Continent {
private final String continentCode;
public Continent(String continentCode) {
this.continentCode = continentCode;
}
// As long as the entity reports the same continent, we are good
// I still don't know whether it's a City or country etc, so it
// ticks the box of being type agnostic at this point.
public boolean contains(ContinentEntity continentEntity) {
return this.equals(continentEntity.getContinent());
}
#Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Continent) obj).continentCode.equals(continentCode);
}
#Override
public int hashCode() {
return continentCode.hashCode();
}
}
public final class Country implements ContinentEntity {
// Could be the code, but consider this over stringly type
private final Continent continent;
private final String coutryCode;
Country(Continent continent, String coutryCode) {
this.continent = continent;
this.coutryCode = coutryCode;
}
#Override
public Continent getContinent() {
return continent;
}
public boolean contains(CountryEntity countryEntity) {
return this.equals(countryEntity.getCountry());
}
#Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Country) obj).coutryCode.equals(coutryCode);
}
#Override
public int hashCode() {
return coutryCode.hashCode();
}
}
public final class City implements CountryEntity {
private final Country country;
private final String name;
public City(Country country, String name) {
this.country = country;
this.name = name;
}
#Override
public Country getCountry() {
return country;
}
}
So you can define a new class:
public final class Village implements CountryEntity {
private final Country country;
private final String name;
public Village(Country country, String name) {
this.country = country;
this.name = name;
}
#Override
public Country getCountry() {
return country;
}
}
and you can already test country.contains(village) or continient.contains(village) without modifying any existing classes. That is a good sign that we've designed correctly. See the OCP.
So how does that look in usage:
Continent europe = new Continent("EUROPE");
Country uk = new Country(europe, "UK");
City london = new City(uk, "London");
// Sensible questions compile:
europe.contains(uk);
uk.contains(london);
europe.contains(london);
// Non-sense does not even compile - yay for typesafety for free:
uk.contains(europe);
This is my final code, in case it helps someone.
public interface GeoEntity {
Optional<GeoEntity> getParent();
boolean contains(GeoEntity child);
}
public interface ContinentEntity extends GeoEntity {
Continent getContinent();
}
public interface CountryEntity extends ContinentEntity {
Country getCountry();
default Continent getContinent() {
return getCountry().getContinent();
}
}
public class Continent implements GeoEntity {
private final String continentCode;
public Continent(String continentCode) {
this.continentCode = continentCode;
}
public boolean contains(ContinentEntity continentEntity) {
return this.equals(continentEntity.getContinent());
}
#Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Continent) obj).continentCode.equals(continentCode);
}
#Override
public int hashCode() {
return continentCode.hashCode();
}
#Override
public Optional<GeoEntity> getParent() {
return Optional.empty();
}
#Override
public boolean contains(GeoEntity child) {
if(!ContinentEntity.class.isAssignableFrom(child.getClass())) {
return false;
}
ContinentEntity continentEntity = (ContinentEntity) child;
return contains(continentEntity);
}
}
public class Country implements ContinentEntity {
final Continent continent;
final String countryCode;
public Country(String continentCode, String countryCode) {
this.continent = new Continent(continentCode);
this.countryCode = countryCode;
}
#Override
public Continent getContinent() {
return continent;
}
public boolean contains(CountryEntity countryEntity) {
return this.equals(countryEntity.getCountry());
}
#Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) return false;
return ((Country) obj).countryCode.equals(countryCode);
}
#Override
public int hashCode() {
return countryCode.hashCode();
}
#Override
public Optional<GeoEntity> getParent() {
return Optional.of(continent);
}
#Override
public boolean contains(GeoEntity child) {
if(!CountryEntity.class.isAssignableFrom(child.getClass())) {
return false;
}
CountryEntity countryEntity = (CountryEntity) child;
return contains(countryEntity);
}
}
Use:
public class Mock {
public static void main(String...args) {
GeoEntity geo = new Continent("EU");
GeoEntity geo2 = new Country("EU", "FR");
//returns true
System.out.println(geo.contains(geo2));
//returns false
System.out.println(geo2.contains(geo));
}
}

Jackson - Deserialize with JsonView

I am trying to restrict which properties from a JSON object are deserialised using Jackson JSONViews. The aim is to use this to prevent consumers of my API from submitting data that they shouldn't.
The problem is, I have either misunderstood JSONViews or I am doing something wrong. See below.
I started trying to do this in Spring but have noticed that even the simple test below doesn't work.
Account Class
public class Account {
#Id
private String id;
private String name;
private List<String> items;
private List<User> users;
#JsonView(AccountViews.Private.class)
public void setId(String id) {
this.id = id;
}
#JsonView(AccountViews.Public.class)
public void setName(String name) {
this.name = name;
}
#JsonView(AccountViews.Public.class)
public void setItems(List<String> items) {
this.items = items;
}
#JsonView(AccountViews.Private.class)
public void setUsers(List<User> users) {
this.users = users;
}
}
Views
public class AccountViews {
public interface Public {}
public interface Private extends Public {}
}
Test
#Test
public void testDeserialization(){
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
Account account = mapper.readerWithView(AccountViews.Public.class).forType(Account.class).readValue("{ \"name\": \"account1\", \"items\": [\"item1\"], \"users\": [ { \"firstname\": \"user1_firstname\", \"lastname\": \"user1_lastname\" }] }");
assertEquals(account.getName(), "account1");
assertNull(account.getUsers());
}
Unforunately, the 2nd assertion fails because Users has a user object inside.
Basically, even though "users" is a property of Account, I don't want the value to be deserialized because I have used the JSONView (AccountViews.Public.class). However, whatever I try it always seems to be deserialized and is present on the account object.
Any help much appreciated.
Error
`java.lang.AssertionError: expected null, but was:<[User#609db43b]>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotNull(Assert.java:755)
at org.junit.Assert.assertNull(Assert.java:737)
at org.junit.Assert.assertNull(Assert.java:747)
at`

Jackson JSON serialization of JPA Entities

I have a JPA persistence layer with many #Entity classes which have many OneToMany and ManyToMany relationships.
I want to expose that entities by RestEasy with Jackson2 as serializer to JSON as REST services.
I know about #JsonIdentityInfo for resolving circular references.
The problem is in different REST services I need to expose different subsets of Entity fields. Moreover I need to expose different levels of depts for collections (OneToMany, OneToOne etc).
For example for this simple Entities:
class User {
Long id;
String name;
Company company;
}
class Company {
Long id;
String name;
List<User> users;
List<Product> products;
}
class Product {
Long id;
String name;
List<User> users;
}
and this REST service:
class MyResource {
User getUser() { //... }
List<User> getUsers() { //... }
Company getCompany() { //... }
List<Company> getComanies() { //... }
}
In method getUser() I need to return JSON with full User object including inner Company object. But that company of course only need to include their id and name field and not full list of users. Even more important that inner Company JSON must not include products! It is logical. If we get the user we don't need products of company that related to this user. If we need them we will send another REST request.
But in method getCompany() I need to return JSON with full Company object including inner JSON arrays of User and Product objects. Of course this time that User objects doesn't need to include inner JSON for Company object.
For this reason I can't use #JsonIgnore. In one case we need some field and in another we doesn't.
Now I came up with approach of using Jackson views (#JsonView annotation). I have View class with different views for every MyResource getter.
public class Views {
public static class User {}
public static class Users {}
public static class Company {}
public static class Companies {}
// etc...
}
and MyResoruce class as
class MyResource {
#JsonView(Views.User.class)
User getUser() { //... }
#JsonView(Views.Users.class)
List<User> getUsers() { //... }
#JsonView(Views.Company.class)
Company getCompany() { //... }
#JsonView(Views.Companies.class)
List<Company> getComanies() { //... }
}
and have a MixIn classes for every Entity with every field annotated as
public abstract class UserMixIn {
#JsonView({ Views.User.class, Views.Users.class, Views.Company.class, Views.Companies.class })
public abstract Long getId();
#JsonView({ Views.User.class, Views.Users.class, Views.Company.class, Views.Companies.class })
public abstract String getName();
#JsonView({ Views.User.class, Views.Users.class })
public abstract Company getCompany();
}
public abstract class CompanyMixIn {
#JsonView({ Views.Company.class, Views.Companies.class, Views.User.class, Views.Users.class })
public abstract Long getId();
#JsonView({ Views.Company.class, Views.Companies.class, Views.User.class, Views.Users.class })
public abstract String getName();
#JsonView({ Views.Company.class, Views.Companies.class })
public abstract List<User> getUsers();
#JsonView({ Views.Company.class, Views.Companies.class })
public abstract List<Product> getProducts();
}
public abstract class ProductMixIn {
#JsonView({ Views.Company.class, Views.Companies.class })
public abstract Long getId();
#JsonView({ Views.Company.class, Views.Companies.class })
public abstract String getName();
public abstract List<User> getUsers();
}
Plurals for support cases where getUsers() doesn't need full inner Company object for every user (performance).
Of course there are just example classes. Real classes are much bigger and complex.
I do not like this approach because I am afraid that in the future it can be a nightmare (too many not manageable views). Maybe there are common approach for exposing JPA Entities as REST services? I believe it is a fairly common task. But can not find any intelligible information on how others doing this. Maybe some best practices.
Your service layer (and your REST controller layer) must expose DTOs (Data transfer objects) instead of #Entity objects.
Example :
For a Service 1 (which focus on User managment) :
public class UserDto {
private Long id;
private String name;
private CompanyDtoLight company;
}
public class CompanyDtoLight {
private Long id;
private String name;
}
For a Service 2 (which focus on Company managment) :
public class CompanyDto {
private Long id;
private String name;
List<UserDtoLight > users;
List<ProductDtoLight > products;
}
public class UserDtoLight {
private Long id;
private String name;
}
class ProductDtoLight {
private Long id;
private String name;
}
(The naming of your DTOs is yours)
How to :
You will need Mappers to transfom and reverse your #Entity to DTOs. Some lib exist like Dozer or MapStruct (there are plenty of other).

OO polymorphism design

What is the best way to do the following:
Suppose I have a class called Person and many derived classes for specialized persons.
Suppose at the beginning of my app, I know I have to deal with a person but I won't know what kind of person it is until much later (something beyond my control so I cannot determine the Person type at the beginning).
So at the beginning I will create a Person and fill in attributes for it. Later, when I know what kind of Person it is, I would instantiate a specialized person and copy over the any saved attributes for her.
Is there a more elegant way to do this without creating two objects?
If you don't know the type of person up front, you won't be able to avoid instantiating two objects. There has to be something to contain the base Person attributes before you know the specialized person, but you can't take advantage of polymorphism without instantiating the specialized object later.
One option is to use a composition pattern, in which each specialized person contains a Person instance rather than inheriting from it. You still have to instantiate two objects, but you don't have to rewrite the code to copy over the saved attributes every time. Here's an example (C# syntax):
public interface IPerson
{
string Name { get; }
int Age { get; }
}
public class Person : IPerson
{
public string Name { get; private set; }
public int Age { get; private set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
public abstract class SpecialPersonBase : IPerson
{
private IPerson myPerson;
protected SpecialPersonBase(IPerson person)
{
myPerson = person;
}
public string Name { get { return myPerson.Name; } }
public int Age { get { return myPerson.Age; } }
public abstract string Greet();
}
public class Doctor : SpecialPersonBase
{
public Doctor(IPerson person) : base(person) { }
public override string Greet()
{
return "How are you feeling?";
}
}
public class Accountant : SpecialPersonBase
{
public Accountant(IPerson person) : base(person) { }
public override string Greet()
{
return "How are your finances?";
}
}
You could use the classes like this:
IPerson bob = new Person("Bob", "25");
// Do things with the generic object
// until you can determine the specific type
SpecialPerson specialBob;
if (bobIsDoctor)
{
specialBob = new Doctor(bob);
}
else if (bobisAccountant)
{
specialBob = new Accountant(bob);
}
specialBob.Greet();

How to persist an enum using NHibernate

Is there a way to persist an enum to the DB using NHibernate? That is have a table of both the code and the name of each value in the enum.
I want to keep the enum without an entity, but still have a foreign key (the int representation of the enum) from all other referencing entities to the enum's table.
Why are you guys over complicating this? It is really simple.
The mapping looks like this:
<property name="OrganizationType"></property>
The model property looks like this:
public virtual OrganizationTypes OrganizationType { get; set; }
The Enum looks like this:
public enum OrganizationTypes
{
NonProfit = 1,
ForProfit = 2
}
NHibernate will automatically figure it all out. Why type more than you need????
You can use the enum type directly: http://web.archive.org/web/20100225131716/http://graysmatter.codivation.com/post/Justice-Grays-NHibernate-War-Stories-Dont-Use-Int-If-You-Mean-Enum.aspx. If your underlying type is a string, it should use the string representation, if it is numeric, it will just use the numeric representation.
But your question wording sounds like you're looking for something different, not quite an enum. It seems that you want a lookup table without creating a separate entity class. I don't think this can be done without creating a separate entity class though.
An easy but not so beautiful solution:
Create an integer field with and set the mapping in the mapping file to the field.
Create a public property that uses the integer field.
private int myField;
public virtual MyEnum MyProperty
{
get { return (MyEnum)myField; }
set { myField = value; }
}
I am using NHibernate 3.2, and this works great:
type="NHibernate.Type.EnumStringType`1[[enum_full_type_name, enum_assembly]], NHibernate"
Not sure when the generic EnumStringType got added, though.
Try using a stategy pattern. Uou can then put logic into your inner classes. I use this quite alot espically when there is logic that should be contained in the "enum". For example the code below has the abstract IsReadyForSubmission() which is then implemented in each of the nested subclasses (only one shown). HTH
[Serializable]
public abstract partial class TimesheetStatus : IHasIdentity<int>
{
public static readonly TimesheetStatus NotEntered = new NotEnteredTimesheetStatus();
public static readonly TimesheetStatus Draft = new DraftTimesheetStatus();
public static readonly TimesheetStatus Submitted = new SubmittedTimesheetStatus();
//etc
public abstract int Id { get; protected set; }
public abstract string Description { get; protected set; }
public abstract bool IsReadyForSubmission();
protected class NotEnteredTimesheetStatus: TimesheetStatus
{
private const string DESCRIPTION = "NotEntered";
private const int ID = 0;
public override int Id
{
get { return ID; }
protected set { if (value != ID)throw new InvalidOperationException("ID for NotEnteredTimesheetStatus must be " + ID); }
}
public override string Description
{
get { return DESCRIPTION; }
protected set { if (value != DESCRIPTION)throw new InvalidOperationException("The description for NotEnteredTimesheetStatus must be " + DESCRIPTION); }
}
public override bool IsReadyForSubmission()
{
return false;
}
}
//etc
}