morphia與spring的整合
阿新 • • 發佈:2018-12-24
簡單的來說Morphia與MongoDB的關係就如Hibernate與關係資料庫的關係, 是一個實現Java物件到MongoDB雙向對映的類庫。
首先我們需要一個生成和配置mongodb的工廠類:
[java] view plain copy print?- publicclass MongoFactoryBean extends AbstractFactoryBean<Mongo> {
- // 表示伺服器列表(主從複製或者分片)的字串陣列
- private String[] serverStrings;
- // mongoDB配置物件
- private
- // 是否主從分離(讀取從庫),預設讀寫都在主庫
- privateboolean readSecondary = false;
- // 設定寫策略(出錯時是否拋異常),預設採用SAFE模式(需要拋異常)
- private WriteConcern writeConcern = WriteConcern.SAFE;
- @Override
- public Class<?> getObjectType() {
- return Mongo.class;
- }
- @Override
- protected Mongo createInstance() throws Exception {
- Mongo mongo = initMongo();
- // 設定主從分離
- if (readSecondary) {
- mongo.setReadPreference(ReadPreference.secondaryPreferred());
- }
- // 設定寫策略
- mongo.setWriteConcern(writeConcern);
- return mongo;
- }
- /**
- * 初始化mongo例項
- * @return
- * @throws Exception
- */
- private Mongo initMongo() throws Exception {
- // 根據條件建立Mongo例項
- Mongo mongo = null;
- List<ServerAddress> serverList = getServerList();
- if (serverList.size() == 0) {
- mongo = new Mongo();
- }elseif(serverList.size() == 1){
- if (mongoOptions != null) {
- mongo = new Mongo(serverList.get(0), mongoOptions);
- }else{
- mongo = new Mongo(serverList.get(0));
- }
- }else{
- if (mongoOptions != null) {
- mongo = new Mongo(serverList, mongoOptions);
- }else{
- mongo = new Mongo(serverList);
- }
- }
- return mongo;
- }
- /**
- * 根據伺服器字串列表,解析出伺服器物件列表
- * <p>
- *
- * @Title: getServerList
- * </p>
- *
- * @return
- * @throws Exception
- */
- private List<ServerAddress> getServerList() throws Exception {
- List<ServerAddress> serverList = new ArrayList<ServerAddress>();
- try {
- for (String serverString : serverStrings) {
- String[] temp = serverString.split(":");
- String host = temp[0];
- if (temp.length > 2) {
- thrownew IllegalArgumentException(
- "Invalid server address string: " + serverString);
- }
- if (temp.length == 2) {
- serverList.add(new ServerAddress(host, Integer
- .parseInt(temp[1])));
- } else {
- serverList.add(new ServerAddress(host));
- }
- }
- return serverList;
- } catch (Exception e) {
- thrownew Exception(
- "Error while converting serverString to ServerAddressList",
- e);
- }
- }
- /* ------------------- setters --------------------- */
- }
public class MongoFactoryBean extends AbstractFactoryBean<Mongo> {
// 表示伺服器列表(主從複製或者分片)的字串陣列
private String[] serverStrings;
// mongoDB配置物件
private MongoOptions mongoOptions;
// 是否主從分離(讀取從庫),預設讀寫都在主庫
private boolean readSecondary = false;
// 設定寫策略(出錯時是否拋異常),預設採用SAFE模式(需要拋異常)
private WriteConcern writeConcern = WriteConcern.SAFE;
@Override
public Class<?> getObjectType() {
return Mongo.class;
}
@Override
protected Mongo createInstance() throws Exception {
Mongo mongo = initMongo();
// 設定主從分離
if (readSecondary) {
mongo.setReadPreference(ReadPreference.secondaryPreferred());
}
// 設定寫策略
mongo.setWriteConcern(writeConcern);
return mongo;
}
/**
* 初始化mongo例項
* @return
* @throws Exception
*/
private Mongo initMongo() throws Exception {
// 根據條件建立Mongo例項
Mongo mongo = null;
List<ServerAddress> serverList = getServerList();
if (serverList.size() == 0) {
mongo = new Mongo();
}else if(serverList.size() == 1){
if (mongoOptions != null) {
mongo = new Mongo(serverList.get(0), mongoOptions);
}else{
mongo = new Mongo(serverList.get(0));
}
}else{
if (mongoOptions != null) {
mongo = new Mongo(serverList, mongoOptions);
}else{
mongo = new Mongo(serverList);
}
}
return mongo;
}
/**
* 根據伺服器字串列表,解析出伺服器物件列表
* <p>
*
* @Title: getServerList
* </p>
*
* @return
* @throws Exception
*/
private List<ServerAddress> getServerList() throws Exception {
List<ServerAddress> serverList = new ArrayList<ServerAddress>();
try {
for (String serverString : serverStrings) {
String[] temp = serverString.split(":");
String host = temp[0];
if (temp.length > 2) {
throw new IllegalArgumentException(
"Invalid server address string: " + serverString);
}
if (temp.length == 2) {
serverList.add(new ServerAddress(host, Integer
.parseInt(temp[1])));
} else {
serverList.add(new ServerAddress(host));
}
}
return serverList;
} catch (Exception e) {
throw new Exception(
"Error while converting serverString to ServerAddressList",
e);
}
}
/* ------------------- setters --------------------- */
}
其次我們需要一個產生和配置morphia物件的工廠類:
[java] view plain copy print?- publicclass MorphiaFactoryBean extends AbstractFactoryBean<Morphia> {
- /**
- * 要掃描並對映的包
- */
- private String[] mapPackages;
- /**
- * 要對映的類
- */
- private String[] mapClasses;
- /**
- * 掃描包時,是否忽略不對映的類
- * 這裡按照Morphia的原始定義,預設設為false
- */
- privateboolean ignoreInvalidClasses;
- @Override
- protected Morphia createInstance() throws Exception {
- Morphia m = new Morphia();
- if (mapPackages != null) {
- for (String packageName : mapPackages) {
- m.mapPackage(packageName, ignoreInvalidClasses);
- }
- }
- if (mapClasses != null) {
- for (String entityClass : mapClasses) {
- m.map(Class.forName(entityClass));
- }
- }
- return m;
- }
- @Override
- public Class<?> getObjectType() {
- return Morphia.class;
- }
- /*----------------------setters-----------------------*/
- }
public class MorphiaFactoryBean extends AbstractFactoryBean<Morphia> {
/**
* 要掃描並對映的包
*/
private String[] mapPackages;
/**
* 要對映的類
*/
private String[] mapClasses;
/**
* 掃描包時,是否忽略不對映的類
* 這裡按照Morphia的原始定義,預設設為false
*/
private boolean ignoreInvalidClasses;
@Override
protected Morphia createInstance() throws Exception {
Morphia m = new Morphia();
if (mapPackages != null) {
for (String packageName : mapPackages) {
m.mapPackage(packageName, ignoreInvalidClasses);
}
}
if (mapClasses != null) {
for (String entityClass : mapClasses) {
m.map(Class.forName(entityClass));
}
}
return m;
}
@Override
public Class<?> getObjectType() {
return Morphia.class;
}
/*----------------------setters-----------------------*/
}
最後我們還需要一個產生和配置Datastore的工廠類:
- publicclass DatastoreFactoryBean extends AbstractFactoryBean<Datastore> {
- private Morphia morphia; //morphia例項,最好是單例
- private Mongo mongo; //mongo例項,最好是單例
- private String dbName; //資料庫名
- private String username; //使用者名稱,可為空
- private String password; //密碼,可為空
- privateboolean toEnsureIndexes=false; //是否確認索引存在,預設false
- privateboolean toEnsureCaps=false; //是否確認caps存在,預設false
- @Override
- protected Datastore createInstance() throws Exception {
- //這裡的username和password可以為null,morphia物件會去處理
- Datastore ds = morphia.createDatastore(mongo, dbName, username,
- password==null?null:password.toCharArray());
- if(toEnsureIndexes){
- ds.ensureIndexes();
- }
- if(toEnsureCaps){
- ds.ensureCaps();
- }
- return ds;
- }
- @Override
- public Class<?> getObjectType() {
- return Datastore.class;
- }
- @Override
- publicvoid afterPropertiesSet() throws Exception {
- super.afterPropertiesSet();
- if (mongo == null) {
- thrownew IllegalStateException("mongo is not set");
- }
- if (morphia == null) {
- thrownew IllegalStateException("morphia is not set");
- }
- }
- /*----------------------setters-----------------------*/
- }
public class DatastoreFactoryBean extends AbstractFactoryBean<Datastore> {
private Morphia morphia; //morphia例項,最好是單例
private Mongo mongo; //mongo例項,最好是單例
private String dbName; //資料庫名
private String username; //使用者名稱,可為空
private String password; //密碼,可為空
private boolean toEnsureIndexes=false; //是否確認索引存在,預設false
private boolean toEnsureCaps=false; //是否確認caps存在,預設false
@Override
protected Datastore createInstance() throws Exception {
//這裡的username和password可以為null,morphia物件會去處理
Datastore ds = morphia.createDatastore(mongo, dbName, username,
password==null?null:password.toCharArray());
if(toEnsureIndexes){
ds.ensureIndexes();
}
if(toEnsureCaps){
ds.ensureCaps();
}
return ds;
}
@Override
public Class<?> getObjectType() {
return Datastore.class;
}
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
if (mongo == null) {
throw new IllegalStateException("mongo is not set");
}
if (morphia == null) {
throw new IllegalStateException("morphia is not set");
}
}
/*----------------------setters-----------------------*/
}
我們來仿照morphia文件,寫兩個測試的POJO:
- @Entity
- publicclass Hotel {
- @Idprivate ObjectId id;
- private String name;
- privateint stars;
- @Embedded
- private Address address;
- /*-----------gettters & setters----------*/
- }
@Entity
public class Hotel {
@Id private ObjectId id;
private String name;
private int stars;
@Embedded
private Address address;
/*-----------gettters & setters----------*/
}
[java]
view plain
copy
print?
- @Embedded
- publicclass Address {
- private String street;
- private String city;
- private String postCode;
- private String country;
- /*-----------gettters & setters----------*/
- }
@Embedded
public class Address {
private String street;
private String city;
private String postCode;
private String country;
/*-----------gettters & setters----------*/
}
還需要一個為測試POJO專門服務的DAO,這裡繼承morphia裡的BasicDAO:
[java] view plain copy print?- publicclass HotelDAO extends BasicDAO<Hotel, ObjectId> {
- protected HotelDAO(Datastore ds) {
- super(ds);
- }
- /* ----------------以下是自定義的資料查詢方法(finder)----------------- */
- }
public class HotelDAO extends BasicDAO<Hotel, ObjectId> {
protected HotelDAO(Datastore ds) {
super(ds);
}
/* ----------------以下是自定義的資料查詢方法(finder)----------------- */
}
最後是spring的XML檔案:
- <?xmlversion="1.0"encoding="UTF-8"?>
- <beansxmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
- <!-- 配置檔案 -->
- <context:property-placeholderlocation="classpath:config.properties"/>
- <!-- mongoDB的配置物件 -->
- <beanid="mongoOptions"class="com.mongodb.MongoOptions">
- <!-- 伺服器是否自動重連,預設為false -->
- <propertyname="autoConnectRetry"value="false"/>
- <!-- 對同一個伺服器嘗試重連的時間(毫秒),設為0時預設使用15秒 -->
- <propertyname="maxAutoConnectRetryTime"value="0"/>
- <!-- 與每個主機的連線數,預設為10 -->
- <propertyname="connectionsPerHost"value="10"/>
- <!-- 連線超時時間(毫秒),預設為10000 -->
- <propertyname="connectTimeout"value="10000"/>
- <!-- 是否建立一個finalize方法,以便在客戶端沒有關閉DBCursor的例項時,清理掉它。預設為true -->
- <propertyname="cursorFinalizerEnabled"value="true"/>
- <!-- 執行緒等待連線可用的最大時間(毫秒),預設為120000 -->
- <propertyname="maxWaitTime"value="120000"/>
- <!-- 可等待執行緒倍數,預設為5.例如connectionsPerHost最大允許10個連線,則10*5=50個執行緒可以等待,更多的執行緒將直接拋異常 -->
- <propertyname="threadsAllowedToBlockForConnectionMultiplier"value="5"/>
- <!-- socket讀寫時超時時間(毫秒),預設為0,不超時 -->
- <propertyname="socketTimeout"value="0"/>
- <!-- 是socket連線在防火牆上保持活動的特性,預設為false -->
- <propertyname="socketKeepAlive"value="false"/>
- <!-- 對應全域性的WriteConcern.SAFE,預設為false -->
- <propertyname="safe"value="true"/>
- <!-- 對應全域性的WriteConcern中的w,預設為0 -->
- <propertyname="w"value="0"/>
- <!-- 對應全域性的WriteConcern中的wtimeout,預設為0 -->
- <propertyname="wtimeout"value="0"/>
- <!-- 對應全域性的WriteConcern.FSYNC_SAFE,如果為真,每次寫入要等待寫入磁碟,預設為false -->
- <propertyname="fsync"value="false"/>
- <!-- 對應全域性的WriteConcern.JOURNAL_SAFE,如果為真,每次寫入要等待日誌檔案寫入磁碟,預設為false -->
- <propertyname="j"value="false"/>
- </bean>
- <!-- 使用工廠建立mongo例項 -->
- <beanid="mongo"class="me.watchzerg.test.morphia.spring.MongoFactoryBean">
- <!-- mongoDB的配置物件 -->
- <propertyname="mongoOptions"ref="mongoOptions"/>
- <!-- 是否主從分離(讀取從庫),預設為false,讀寫都在主庫 -->
- <propertyname="readSecondary"value="false"/>
- <!-- 設定寫策略,預設為WriteConcern.SAFE,優先順序高於mongoOptions中的safe -->
- <propertyname="writeConcern"value="SAFE"/>
- <!-- 設定伺服器列表,預設為localhost:27017 -->
- <propertyname="serverStrings">
- <array>
- <value>${mongoDB.server}</value>
- </array>
- </property>
- </bean>
- <!-- 使用工廠建立morphia例項,同時完成類對映操作 -->
- <beanid="morphia"class="me.watchzerg.test.morphia.spring.MorphiaFactoryBean">
- <!-- 指定要掃描的POJO包路徑 -->
- <propertyname="mapPackages">
- <array>
- <value>me.watchzerg.test.morphia.pojo</value>
- </array>
- </property>
- <!-- 指定要對映的類 -->
- <!-- <propertyname="mapClasses">
- <array>
- <value>me.watchzerg.test.morphia.pojo.Hotel</value>
- <value>me.watchzerg.test.morphia.pojo.Address</value>
- </array>
- </property> -->
- <!-- 掃描包時是否忽略不可用的類,預設為false -->
- <!-- <property name="ignoreInvalidClasses" value="false"/> -->
- </bean>
- <!-- 使用工廠建立datastore,同時完成index和caps的確認操作 -->
- <beanid="datastore"class="me.watchzerg.test.morphia.spring.DatastoreFactoryBean">
- <propertyname="morphia"ref="morphia"/>
- <propertyname="mongo"ref="mongo"/>
- <!-- collection的名稱 -->
- <propertyname="dbName"value="${mongoDB.dbName}"/>
- <!-- 使用者名稱和密碼可以為空 -->
- <!-- <propertyname="username"value="my_username"/>
- <propertyname="password"value="my_password"/> -->
- <!-- 是否進行index和caps的確認操作,預設為flase -->
- <propertyname="toEnsureIndexes"value="true"/>
- <propertyname="toEnsureCaps"value="true"/>
- </bean>
- <!-- ===============以下是具體DAO的實現===================== -->
- <beanid="hotelDAO"class="me.watchzerg.test.morphia.dao.impl.HotelDAO">
- <constructor-argref="datastore"/>
- </bean>
- </beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 配置檔案 -->
<context:property-placeholder location="classpath:config.properties" />
<!-- mongoDB的配置物件 -->
<bean id="mongoOptions" class="com.mongodb.MongoOptions">
<!-- 伺服器是否自動重連,預設為false -->
<property name="autoConnectRetry" value="false" />
<!-- 對同一個伺服器嘗試重連的時間(毫秒),設為0時預設使用15秒 -->
<property name="maxAutoConnectRetryTime" value="0" />
<!-- 與每個主機的連線數,預設為10 -->
<property name="connectionsPerHost" value="10" />
<!-- 連線超時時間(毫秒),預設為10000 -->
<property name="connectTimeout" value="10000" />
<!-- 是否建立一個finalize方法,以便在客戶端沒有關閉DBCursor的例項時,清理掉它。預設為true -->
<property name="cursorFinalizerEnabled" value="true" />
<!-- 執行緒等待連線可用的最大時間(毫秒),預設為120000 -->
<property name="maxWaitTime" value="120000" />
<!-- 可等待執行緒倍數,預設為5.例如connectionsPerHost最大允許10個連線,則10*5=50個執行緒可以等待,更多的執行緒將直接拋異常 -->
<property name="threadsAllowedToBlockForConnectionMultiplier" value="5" />
<!-- socket讀寫時超時時間(毫秒),預設為0,不超時 -->
<property name="socketTimeout" value="0" />
<!-- 是socket連線在防火牆上保持活動的特性,預設為false -->
<property name="socketKeepAlive" value="false" />
<!-- 對應全域性的WriteConcern.SAFE,預設為false -->
<property name="safe" value="true" />
<!-- 對應全域性的WriteConcern中的w,預設為0 -->
<property name="w" value="0" />
<!-- 對應全域性的WriteConcern中的wtimeout,預設為0 -->
<property name="wtimeout" value="0" />
<!-- 對應全域性的WriteConcern.FSYNC_SAFE,如果為真,每次寫入要等待寫入磁碟,預設為false -->
<property name="fsync" value="false" />
<!-- 對應全域性的WriteConcern.JOURNAL_SAFE,如果為真,每次寫入要等待日誌檔案寫入磁碟,預設為false -->
<property name="j" value="false" />
</bean>
<!-- 使用工廠建立mongo例項 -->
<bean id="mongo" class="me.watchzerg.test.morphia.spring.MongoFactoryBean">
<!-- mongoDB的配置物件 -->
<property name="mongoOptions" ref="mongoOptions"/>
<!-- 是否主從分離(讀取從庫),預設為false,讀寫都在主庫 -->
<property name="readSecondary" value="false"/>
<!-- 設定寫策略,預設為WriteConcern.SAFE,優先順序高於mongoOptions中的safe -->
<property name="writeConcern" value="SAFE"/>
<!-- 設定伺服器列表,預設為localhost:27017 -->
<property name="serverStrings">
<array>
<value>${mongoDB.server}</value>
</array>
</property>
</bean>
<!-- 使用工廠建立morphia例項,同時完成類對映操作 -->
<bean id="morphia" class="me.watchzerg.test.morphia.spring.MorphiaFactoryBean" >
<!-- 指定要掃描的POJO包路徑 -->
<property name="mapPackages">
<array>
<value>me.watchzerg.test.morphia.pojo</value>
</array>
</property>
<!-- 指定要對映的類 -->
<!-- <property name="mapClasses">
<array>
<value>me.watchzerg.test.morphia.pojo.Hotel</value>
<value>me.watchzerg.test.morphia.pojo.Address</value>
</array>
</property> -->
<!-- 掃描包時是否忽略不可用的類,預設為false -->
<!-- <property name="ignoreInvalidClasses" value="false"/> -->
</bean>
<!-- 使用工廠建立datastore,同時完成index和caps的確認操作 -->
<bean id="datastore" class="me.watchzerg.test.morphia.spring.DatastoreFactoryBean" >
<property name="morphia" ref="morphia"/>
<property name="mongo" ref="mongo"/>
<!-- collection的名稱 -->
<property name="dbName" value="${mongoDB.dbName}"/>
<!-- 使用者名稱和密碼可以為空 -->
<!-- <property name="username" value="my_username"/>
<property name="password" value="my_password"/> -->
<!-- 是否進行index和caps的確認操作,預設為flase -->
<property name="toEnsureIndexes" value="true"/>
<property name="toEnsureCaps" value="true"/>
</bean>
<!-- ===============以下是具體DAO的實現===================== -->
<bean id="hotelDAO" class="me.watchzerg.test.morphia.dao.impl.Hot