Over a million developers have joined DZone.

Java EE + MongoDb with Apache TomEE and Jongo Starter Project

· Java Zone

Discover how powerful static code analysis and ergonomic design make development not only productive but also an enjoyable experience, brought to you in partnership with JetBrains

Know MongoDB and Java EE, but you don't know exactly how to integrate both of them? Do you read a lot about the topic but you have not found a solution which fits this purpose? This starter project is for you:

You will learn how to use MongoDB and Java EE in a fashion way without having to depend on Spring Data MongoDB framework but with "similar" basic features.

The only thing better than a Maven archetype is a repository you can fork with everything already setup. Skip the documentation and just fork-and-code. This starter project contains:

The example is pretty simple, we want to store colors inside a MongoDB collection.

Our POJO is like:

public class Color {

	@ObjectId
	private String _id;
	
	private String name;
	private int r;
	private int g;
	private int b;
	
	public Color() {
		super();
	}

	public Color(String name, int r, int g, int b) {
		super();
		this.name = name;
		this.r = r;
		this.g = g;
		this.b = b;
	}

        // getters and setters
}

Note that we are using @ObjectId annotation provided by Jongo to set this field as MongoDB id. Moreover because it is called _id, the id will be set automatically.

Then the service layer:

@Singleton
@Lock(LockType.READ)
public abstract class ColorService implements InvocationHandler {

	@JongoCollection("color")
	@Inject
	MongoCollection colorMongoCollection;
	
	@Insert
	public abstract Color createColor(Color c);
	
	@Remove
	public abstract int removeAllColors();
	
	@FindById
	public abstract Color findColorById(String id);
	
	@FindOne("{name:#}")
	public abstract Color findColorByColorName(String colorName);
	
	@Find("{r:#}")
	public abstract Iterable<Color> findColorByRed(int r);
	
	public long countColors() {
		return colorMongoCollection.count();
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		return PersistenceHandler.invoke(colorMongoCollection, method, args);
	}
	
}

Note that there isn't a lot of code, but some points are really interesting stuff. Let's analyze them.

@Singleton is used to define an EJB as singleton, it works with @Stateless as well, for Java EE users no news here.

The class is abstract. Why? Because it allows us to not implement all methods but define them.

Also implements java.lang.reflect.InvocationHandler. This is because we want to use one really interesting feature which allows us to create a fallback method called invoke. For any method defined but not implemented this method is called.

We have a MongoCollection class (from Jongo project) that it is injected. MongoCollection represents a collection in MongoDB. Because we need to set which collection we want to work with, an annotation called @JongoCollection is created so you can pass the name of the backend collection. Note that MongoCollection is produced by CDI container by using our custom producer. Again no news here for CDI users.

@ApplicationScoped
public class MongoCollectionProducer {

    @Inject
    DB mongoDb;
    
    Jongo jongo;

    @PostConstruct
    public void initialize() throws UnknownHostException {
        jongo = new Jongo(mongoDb);
    }


    @Produces
    @JongoCollection
    MongoCollection collection(InjectionPoint injectionPoint) {

        JongoCollection jongoCollectionAnnotation = Reflection.annotation(injectionPoint
                .getQualifiers(), JongoCollection.class);

        if(jongoCollectionAnnotation != null) {
            String collectionName = jongoCollectionAnnotation.value();
            return jongo.getCollection(collectionName);
        }

        throw new IllegalArgumentException();
    }


}

Then there is a lot of methods which represents CRUD operations. Note that they are not implemented, they are only annotated with @Insert, @Find, @Remove, ... to set which is the purpose of the method we want to execute. Some of them like finders or removers can receive Jongo-like query to be executed. And also a method called countColors which as you can see you can implement as custom method without relying to logic implemented within invoke method.

And finally the invoke method. This method will be called for all abstract methods, and simply sends to PersistenceHandler class, which in fact is a util class against Jongo to execute the required operation.

And that's all, quite simple, and if you want to add new abstract operations, you only need to implement them inside PersistenceHandler class.

Some of you may wonder why I use annotations and not the typical Spring Data approach where the name of the method indicates the operation. You can implement this approach as well, it is a simple matter of creating a regular expression inside PersistenceHandler class instead of if/else with annotations, but I prefer annotations approach. Faster in execution time, clean, and for example you can refactor the annotation name from @Find to @Buscar (Spanish equivalent) without worrying if you are breaking some regular expression.

And finally the test:

@RunWith(Arquillian.class)
public class ColorTest {

	private static final String MONGODB_RESOURCE = "<resources>\n" + 
			"    <Resource id=\"mongoUri\" class-name=\"com.mongodb.MongoClientURI\" constructor=\"uri\">\n" + 
			"        uri  mongodb://localhost/test\n" + 
			"    </Resource>\n" + 
			"</resources>";
	
    @Deployment
    public static JavaArchive createDeployment() {
        JavaArchive javaArchive = ShrinkWrap.create(JavaArchive.class)
                .addPackages(true, Color.class.getPackage())
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
                .addAsManifestResource(new StringAsset(MONGODB_RESOURCE), "resources.xml")
                .merge(getJongoAndMongoDependecies());
        
        return javaArchive;
    }

    private static JavaArchive getJongoAndMongoDependecies() {
        JavaArchive[] javaArchives = Maven.configureResolver()
                .loadPomFromFile("pom.xml")
                .resolve("org.mongodb:mongo-java-driver", "org.jongo:jongo")
                .withTransitivity()
                .as(JavaArchive.class);

        JavaArchive mergedLibraries = ShrinkWrap.create(JavaArchive.class);

        for (JavaArchive javaArchive : javaArchives) {
            mergedLibraries.merge(javaArchive);
        }

        return mergedLibraries;
    }

    @EJB
    ColorService colorService;

    @Before
    public void cleanDatabase() {
       colorService.removeAllColors();
    }

    @Test
    public void should_insert_color() {

        Color color = colorService.createColor(new Color("red", 255, 0, 0));

        assertThat(color.getId(), notNullValue());
        assertThat(color.getName(), is("red"));
        assertThat(color.getR(), is(255));
        assertThat(color.getB(), is(0));
        assertThat(color.getG(), is(0));

    }


    @Test
    public void should_count_number_of_colors() {

        colorService.createColor(new Color("red", 255, 0, 0));
        colorService.createColor(new Color("blue", 0, 0, 255));

        assertThat(colorService.countColors(), is(2L));

    }

    @Test
    public void should_find_colors_by_id() {

        Color originalColor = colorService.createColor(new Color("red", 255, 0, 0));

        Color color = colorService.findColorById(originalColor.getId());

        assertThat(color.getId(), notNullValue());
        assertThat(color.getName(), is("red"));
        assertThat(color.getR(), is(255));
        assertThat(color.getB(), is(0));
        assertThat(color.getG(), is(0));

    }

    @Test
    public void should_find_colors_by_name() {

        colorService.createColor(new Color("red", 255, 0, 0));

        Color color = colorService.findColorByColorName("red");

        assertThat(color.getId(), notNullValue());
        assertThat(color.getName(), is("red"));
        assertThat(color.getR(), is(255));
        assertThat(color.getB(), is(0));
        assertThat(color.getG(), is(0));


    }

    @Test
    public void should_find_colors_by_red() {

        colorService.createColor(new Color("red", 255, 0, 0));
        colorService.createColor(new Color("white", 255, 255, 255));

        Iterable<Color> colorByRed = colorService.findColorByRed(255);

        assertThat(colorByRed, hasItems(new Color("red", 255, 0, 0), new Color("white", 255, 255, 255)));

    }

}

This is an Arquillian test that has nothing special apart from one line:

.addAsManifestResource(new StringAsset(MONGODB_RESOURCE), "resources.xml")

Because we are using Apache TomEE we use the way it has to configure elements to be used as javax.annotation.Resource in our code.

The META-INF/resources.xml content will be:

<resources> 
  <Resource id="mongoUri" class-name="com.mongodb.MongoClientURI" constructor="uri"> 
	uri  mongodb://localhost/test
  </Resource>
</resources>

and then we use in our MongoClient producer to create the MongoClient instance to be used inside code. Note that we are using @Resource as any standard resource like DataSource, but in fact MongoClientURI is injected:

@ApplicationScoped
public class MongoDBProducer {

	@Resource(name = "mongoUri")
	private MongoClientURI mongoClientURI;
	
	private DB db;

	@PostConstruct
	public void init() throws UnknownHostException {
		MongoClient mongoClient = new MongoClient(mongoClientURI);
		db =  mongoClient.getDB(mongoClientURI.getDatabase());
	}

	@Produces
	public DB createDB() {
		return db;
	}

}

so in fact Mongo connection is configured in META-INF/resources.xml file and thanks of TomEE we can refer it as any standard resource. If you are going to use other application server you can change this approach to the one provided by it, or if you want you can use DeltaSpike extensions or your own method. Also because MongoClient database is get from a method annotated with @Produces  you can be injected it wherever you want on your code, so you can skip the abstract services layer if you want.

What are the benefits of this approach?

First that it is Java EE solution, you can use it without depending on Spring framework or any other library. You implement what you need, you do not download a bunch of libraries simply for accessing a MongoDB with some kind of object mapping.

Also as you may see, the code is quite simple and there is no magic behind it, you can debug it without any problem, or even improve or change depending on your needs. The code is yours and is waiting to be modified. Do you want to use native MongoDB objects instead of Jongo? No problem, you can implement it. Moreover there aren't much layers, in fact only one (the PersistenceHandler) so the solution is pretty fast in terms of execution.

Of course this do not mean that you can't use Spring Data MongoDB. It is a really interesting framework, so if you are already using Spring, go ahead with it, but if you plan to use a full Java EE solution, then clone this project and start using MongoDB without having to make some research on the net about how to integrate both of them.

You can clone the project from https://github.com/lordofthejars/tomee-mongodb-starter-project

We keep learning,

Alex.

Learn more about Kotlin, a new programming language designed to solve problems that software developers face every day brought to you in partnership with JetBrains.

Topics:

Published at DZone with permission of Alex Soto, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}