知道MongoDB和Java EE ,但是您不知道如何将两者集成在一起? 您是否阅读了很多有关该主题的内容,但没有找到适合该目的的解决方案? 这个入门项目适合您:
您将学习如何以一种时尚的方式使用MongoDB和Java EE ,而不必依赖于Spring Data MongoDB框架,但具有“相似的”基本功能。
比Maven原型更好的唯一事情是可以使用已设置的所有内容进行存储的存储库。 跳过文档,然后进行分叉编码。 该入门项目包含:
- Jongo如MongoDB的映射( www.jongo.org )。
- Apache TomEE作为应用程序服务和集成。 ( tomee.apache.org )
- 测试的Arquillian 。 ( www.arquillian.org )
该示例非常简单,我们希望将颜色存储在MongoDB集合中。
我们的POJO就像:
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
}
请注意,我们正在使用Jongo提供的@ObjectId批注将此字段设置为MongoDB id。 此外,由于它称为_id,因此将自动设置id。
然后是服务层:
@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);
}
}
请注意,没有很多代码,但是有些要点确实很有趣。 让我们对其进行分析。
@Singleton用于将EJB定义为单例,它也与@Stateless一起使用,对于Java EE用户而言,这里没有消息。
该类是抽象的。 为什么? 因为它允许我们不实现所有方法,而是定义它们。
还实现java.lang.reflect.InvocationHandler 。 这是因为我们要使用一个非常有趣的功能,该功能允许我们创建一个称为invoke的后备方法。 对于已定义但未实现的任何方法,都会调用此方法。
我们有一个MongoCollection类(来自Jongo项目),已将其注入。 MongoCollection代表MongoDB中的集合。 因为我们需要设置要使用的集合,所以将创建一个名为@JongoCollection的注释,以便您可以传递后端集合的名称。 请注意, MongoCollection是由CDI容器使用我们的自定义生成器生成的。 对于CDI用户,这里也没有消息。
@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();
}
}
然后有很多方法代表CRUD操作。 请注意,他们不执行,他们只用@Insert,@find,@Remove注释,...,以设置这是我们要执行的方法的目的。 其中一些(例如查找器或删除器)可以接收要执行的类似于Jongo的查询。 还有一个名为countColors的方法,您可以看到它可以作为自定义方法实现,而不必依赖于invoke方法中实现的逻辑。
最后是invoke方法。 该方法将为所有抽象方法调用,并简单地发送到PersistenceHandler类,该类实际上是针对Jongo的util类,用于执行所需的操作。
就是这么简单,如果您想添加新的抽象操作,则只需在PersistenceHandler类中实现它们即可。
你们中的有些人可能会奇怪,为什么我使用注释而不是使用典型的Spring Data方法(该方法的名称表示操作)。 您也可以实现这种方法,这是在PersistenceHandler类中创建正则表达式的简单问题,而不是带有注释的if / else,但是我更喜欢注释方法。 执行时间更快,更干净,例如,您可以将批注名称从@Find重构为@Buscar (相当于西班牙文),而不必担心是否破坏了某些正则表达式。
最后是测试:
@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)));
}
}
这是一项Arquillian测试,除一行外没有其他特殊之处:
.addAsManifestResource(新的StringAsset(MONGODB_RESOURCE),“ resources.xml”)
因为我们使用的是Apache TomEE,所以我们使用它必须配置在代码中用作javax.annotation.Resource的元素的方式。
META-INF / resources.xml的内容将是:
<resources>
<Resource id="mongoUri" class-name="com.mongodb.MongoClientURI" constructor="uri">
uri mongodb://localhost/test
</Resource>
</resources>
然后在MongoClient生产者中使用它来创建要在代码内使用的MongoClient实例。 请注意,我们将@Resource用作任何标准资源,例如DataSource ,但实际上注入了MongoClientURI :
@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;
}
}
因此,实际上Mongo连接是在META-INF / resources.xml文件中配置的,感谢TomEE,我们可以将其视为任何标准资源。
如果要使用其他应用程序服务器,则可以将此方法更改为它提供的方法,或者,如果需要,可以使用DeltaSpike扩展或您自己的方法。 另外,由于MongoClient数据库是从带有@Produces注释的方法中获取的,因此您可以在代码上的任何位置将其注入,因此,如果需要,可以跳过抽象服务层。
这种方法有什么好处?
首先,它是Java EE解决方案,您可以在不依赖Spring框架或任何其他库的情况下使用它。 您可以实现所需的内容,而不必下载大量的库,而仅仅是为了通过某种对象映射访问MongoDB 。
就像您可能看到的那样,代码非常简单,并且没有任何魔力,您可以毫无问题地对其进行调试,甚至可以根据需要进行改进或更改。 该代码是您的,正在等待修改。 您是否要使用本机MongoDB对象而不是Jongo ? 没问题,您可以实现它。 而且,层数不多,实际上只有一层( PersistenceHandler ),因此该解决方案的执行速度非常快。
当然,这并不意味着您不能使用Spring Data MongoDB 。 这是一个非常有趣的框架,因此,如果您已经在使用Spring ,请继续进行下去,但是如果您打算使用完整的J ava EE解决方案,请克隆此项目并开始使用MongoDB,而无需进行任何网络研究。关于如何将它们整合在一起的知识。