隨筆 - 22, 文章 - 0, 評論 - 1, 引用 - 0
          數據加載中……

          morphia與spring的整合

          最近研究mongoDB的各種pojo-mapping框架,中意的就兩個:morphia和spring-data-mongodb。
          本來想著spring-data-mongodb與spring的結合更緊密些,但悲劇的是其要求spring3.0.x以上版本,與生產環境不符。查了查stackoverflow,大家評價morphia更老牌更穩定一些,于是就用這個了。
          研究了一番,果然與spring整合起來很麻煩。
          首先看stackoverflow上的帖子,提問者跟我的想法完全一樣:在spring里,我沒有現成的辦法調用ensureIndexes()這樣的方法啊,腫么辦?
          http://stackoverflow.com/questions/5365315/using-morphia-with-spring
          回答者給出的兩個鏈接我也看了,真心沒啥收獲。
          后來又搜到一篇帖子:
          http://topmanopensource.iteye.com/blog/1449889
          很粗略的看了一下,還不錯,總之都得自己實現那些工廠類,完成與spring的集成。
          看來網上這方面的需求還不少,甚至在google-code上找到一個項目叫“spring-morphia”,專門來解決這個問題:
          http://code.google.com/p/spring-morphia/
          貌似荒廢已久,沒有完成的可供下載的jar包,但是在其svn上,可以看到一些可供我們參考的類:
          http://code.google.com/p/spring-morphia/source/browse/trunk/spring-morphia/src/main/java/com/so/smorphia/
          本文基本上就是根據上面兩個連接的思路寫的,自己總結一下而已,不做過多解釋了,代碼里有注釋。

          首先我們需要一個生成和配置mongodb的工廠類:

           1 public class MongoFactoryBean extends AbstractFactoryBean<Mongo> {
           2 
           3     // 表示服務器列表(主從復制或者分片)的字符串數組
           4     private String[] serverStrings;
           5     // mongoDB配置對象
           6     private MongoOptions mongoOptions;
           7     // 是否主從分離(讀取從庫),默認讀寫都在主庫
           8     private boolean readSecondary = false;
           9     // 設定寫策略(出錯時是否拋異常),默認采用SAFE模式(需要拋異常)
          10     private WriteConcern writeConcern = WriteConcern.SAFE;
          11 
          12     @Override
          13     public Class<?> getObjectType() {
          14         return Mongo.class;
          15     }
          16 
          17     @Override
          18     protected Mongo createInstance() throws Exception {
          19         Mongo mongo = initMongo();
          20         
          21         // 設定主從分離
          22         if (readSecondary) {
          23             mongo.setReadPreference(ReadPreference.secondaryPreferred());
          24         }
          25 
          26         // 設定寫策略
          27         mongo.setWriteConcern(writeConcern);
          28         return mongo;
          29     }
          30     
          31     /**
          32      * 初始化mongo實例
          33      * @return
          34      * @throws Exception
          35      */
          36     private Mongo initMongo() throws Exception {
          37         // 根據條件創建Mongo實例
          38         Mongo mongo = null;
          39         List<ServerAddress> serverList = getServerList();
          40 
          41         if (serverList.size() == 0) {
          42             mongo = new Mongo();
          43         }else if(serverList.size() == 1){
          44             if (mongoOptions != null) {
          45                 mongo = new Mongo(serverList.get(0), mongoOptions);
          46             }else{
          47                 mongo = new Mongo(serverList.get(0));
          48             }
          49         }else{
          50             if (mongoOptions != null) {
          51                 mongo = new Mongo(serverList, mongoOptions);
          52             }else{
          53                 mongo = new Mongo(serverList);
          54             }
          55         }
          56         return mongo;
          57     }
          58     
          59     
          60     /**
          61      * 根據服務器字符串列表,解析出服務器對象列表
          62      * <p>
          63      * 
          64      * @Title: getServerList
          65      *         </p>
          66      * 
          67      * @return
          68      * @throws Exception
          69      */
          70     private List<ServerAddress> getServerList() throws Exception {
          71         List<ServerAddress> serverList = new ArrayList<ServerAddress>();
          72         try {
          73             for (String serverString : serverStrings) {
          74                 String[] temp = serverString.split(":");
          75                 String host = temp[0];
          76                 if (temp.length > 2) {
          77                     throw new IllegalArgumentException(
          78                             "Invalid server address string: " + serverString);
          79                 }
          80                 if (temp.length == 2) {
          81                     serverList.add(new ServerAddress(host, Integer
          82                             .parseInt(temp[1])));
          83                 } else {
          84                     serverList.add(new ServerAddress(host));
          85                 }
          86             }
          87             return serverList;
          88         } catch (Exception e) {
          89             throw new Exception(
          90                     "Error while converting serverString to ServerAddressList",
          91                     e);
          92         }
          93     }
          94 
          95     /* ------------------- setters --------------------- */
          96 }

          其次我們需要一個產生和配置morphia對象的工廠類:

           1 public class MorphiaFactoryBean extends AbstractFactoryBean<Morphia> {
           2     /**
           3      * 要掃描并映射的包
           4      */
           5     private String[] mapPackages;  
           6     
           7     /**
           8      * 要映射的類
           9      */
          10     private String[] mapClasses;  
          11     
          12     /**
          13      * 掃描包時,是否忽略不映射的類
          14      * 這里按照Morphia的原始定義,默認設為false
          15      */
          16     private boolean ignoreInvalidClasses;
          17     
          18     @Override
          19     protected Morphia createInstance() throws Exception {
          20         Morphia m = new Morphia();
          21         if (mapPackages != null) {
          22             for (String packageName : mapPackages) {
          23                 m.mapPackage(packageName, ignoreInvalidClasses);
          24             }
          25         }
          26         if (mapClasses != null) {  
          27             for (String entityClass : mapClasses) {
          28                 m.map(Class.forName(entityClass));
          29             }
          30         }
          31         return m;
          32     }
          33 
          34     @Override
          35     public Class<?> getObjectType() {
          36         return Morphia.class;
          37     }
          38     
          39     /*----------------------setters-----------------------*/
          40 }

          最后我們還需要一個產生和配置Datastore的工廠類:

           1 public class DatastoreFactoryBean extends AbstractFactoryBean<Datastore> {
           2     
           3     private Morphia morphia;    //morphia實例,最好是單例
           4     private Mongo mongo;    //mongo實例,最好是單例
           5     private String dbName;    //數據庫名
           6     private String username;    //用戶名,可為空
           7     private String password;    //密碼,可為空
           8     private boolean toEnsureIndexes=false;    //是否確認索引存在,默認false
           9     private boolean toEnsureCaps=false;    //是否確認caps存在,默認false
          10     
          11 
          12     @Override
          13     protected Datastore createInstance() throws Exception {
          14         //這里的username和password可以為null,morphia對象會去處理
          15         Datastore ds = morphia.createDatastore(mongo, dbName, username,
          16                 password==null?null:password.toCharArray());
          17         if(toEnsureIndexes){
          18             ds.ensureIndexes();
          19         }
          20         if(toEnsureCaps){
          21             ds.ensureCaps();
          22         }
          23         return ds;
          24     }
          25 
          26     @Override
          27     public Class<?> getObjectType() {
          28         return Datastore.class;
          29     }
          30 
          31     @Override
          32     public void afterPropertiesSet() throws Exception {
          33         super.afterPropertiesSet();
          34         if (mongo == null) {
          35             throw new IllegalStateException("mongo is not set");
          36         }
          37         if (morphia == null) {
          38             throw new IllegalStateException("morphia is not set");
          39         }
          40     }
          41     
          42     /*----------------------setters-----------------------*/
          43 }

          我們來仿照morphia文檔,寫兩個測試的POJO:

           1 @Entity
           2 public class Hotel {
           3     @Id private ObjectId id;
           4     
           5     private String name;
           6     private int stars;
           7     
           8     @Embedded
           9     private Address address;    
          10     
          11     /*-----------gettters & setters----------*/
          12 }

          1 @Embedded
          2 public class Address {
          3     private String street;
          4     private String city;
          5     private String postCode;
          6     private String country;
          7     /*-----------gettters & setters----------*/
          8 }

          還需要一個為測試POJO專門服務的DAO,這里繼承morphia里的BasicDAO:

          1 public class HotelDAO extends BasicDAO<Hotel, ObjectId> {
          2 
          3     protected HotelDAO(Datastore ds) {
          4         super(ds);
          5     }
          6     
          7     /* ----------------以下是自定義的數據查詢方法(finder)----------------- */
          8 }

          最后是spring的XML文件:

            1 <?xml version="1.0" encoding="UTF-8"?>  
            2 <beans xmlns="http://www.springframework.org/schema/beans"  
            3     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" 
            4     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
            5     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">  
            6     
            7     <!-- 配置文件 -->
            8     <context:property-placeholder location="classpath:config.properties" />
            9     
           10     <!-- mongoDB的配置對象 -->
           11     <bean id="mongoOptions" class="com.mongodb.MongoOptions">
           12         <!-- 服務器是否自動重連,默認為false -->
           13         <property name="autoConnectRetry" value="false" />
           14         <!-- 對同一個服務器嘗試重連的時間(毫秒),設為0時默認使用15秒 -->
           15         <property name="maxAutoConnectRetryTime" value="0" />
           16         <!-- 與每個主機的連接數,默認為10 -->
           17         <property name="connectionsPerHost" value="10" />
           18         <!-- 連接超時時間(毫秒),默認為10000 -->
           19         <property name="connectTimeout" value="10000" />
           20         <!-- 是否創建一個finalize方法,以便在客戶端沒有關閉DBCursor的實例時,清理掉它。默認為true -->
           21         <property name="cursorFinalizerEnabled" value="true" />
           22         <!-- 線程等待連接可用的最大時間(毫秒),默認為120000 -->
           23         <property name="maxWaitTime" value="120000" />
           24         <!-- 可等待線程倍數,默認為5.例如connectionsPerHost最大允許10個連接,則10*5=50個線程可以等待,更多的線程將直接拋異常 -->
           25         <property name="threadsAllowedToBlockForConnectionMultiplier" value="5" />
           26         <!-- socket讀寫時超時時間(毫秒),默認為0,不超時 -->
           27         <property name="socketTimeout" value="0" />
           28         <!-- 是socket連接在防火墻上保持活動的特性,默認為false -->
           29         <property name="socketKeepAlive" value="false" />
           30         <!-- 對應全局的WriteConcern.SAFE,默認為false -->
           31         <property name="safe" value="true" />
           32         <!-- 對應全局的WriteConcern中的w,默認為0 -->
           33         <property name="w" value="0" />
           34         <!-- 對應全局的WriteConcern中的wtimeout,默認為0 -->
           35         <property name="wtimeout" value="0" />
           36         <!-- 對應全局的WriteConcern.FSYNC_SAFE,如果為真,每次寫入要等待寫入磁盤,默認為false -->
           37         <property name="fsync" value="false" />
           38         <!-- 對應全局的WriteConcern.JOURNAL_SAFE,如果為真,每次寫入要等待日志文件寫入磁盤,默認為false -->
           39         <property name="j" value="false" />
           40     </bean>
           41     
           42     <!-- 使用工廠創建mongo實例 -->
           43     <bean id="mongo" class="me.watchzerg.test.morphia.spring.MongoFactoryBean">
           44         <!-- mongoDB的配置對象 -->
           45         <property name="mongoOptions" ref="mongoOptions"/>
           46         
           47         <!-- 是否主從分離(讀取從庫),默認為false,讀寫都在主庫 -->
           48         <property name="readSecondary" value="false"/>
           49         
           50         <!-- 設定寫策略,默認為WriteConcern.SAFE,優先級高于mongoOptions中的safe -->
           51         <property name="writeConcern" value="SAFE"/>
           52         
           53         <!-- 設定服務器列表,默認為localhost:27017 -->
           54         <property name="serverStrings">
           55             <array>
           56                 <value>${mongoDB.server}</value>
           57             </array>
           58         </property>
           59     </bean>
           60     
           61     
           62     <!-- 使用工廠創建morphia實例,同時完成類映射操作 -->
           63     <bean id="morphia" class="me.watchzerg.test.morphia.spring.MorphiaFactoryBean" >
           64         <!-- 指定要掃描的POJO包路徑 -->
           65         <property name="mapPackages">
           66             <array>
           67                 <value>me.watchzerg.test.morphia.pojo</value>
           68             </array>
           69         </property>
           70         
           71         <!-- 指定要映射的類 -->
           72         <!-- <property name="mapClasses">
           73             <array>
           74                 <value>me.watchzerg.test.morphia.pojo.Hotel</value>
           75                 <value>me.watchzerg.test.morphia.pojo.Address</value>
           76             </array>
           77         </property> -->
           78         
           79         <!-- 掃描包時是否忽略不可用的類,默認為false -->
           80         <!-- <property name="ignoreInvalidClasses" value="false"/> -->
           81     </bean>
           82     
           83     <!-- 使用工廠創建datastore,同時完成index和caps的確認操作 -->
           84     <bean id="datastore" class="me.watchzerg.test.morphia.spring.DatastoreFactoryBean" >
           85         <property name="morphia" ref="morphia"/>
           86         <property name="mongo" ref="mongo"/>
           87         
           88         <!-- collection的名稱 -->
           89         <property name="dbName" value="${mongoDB.dbName}"/>
           90         
           91         <!-- 用戶名和密碼可以為空 -->
           92         <!-- <property name="username" value="my_username"/>
           93         <property name="password" value="my_password"/> -->
           94         
           95         <!-- 是否進行index和caps的確認操作,默認為flase -->
           96         <property name="toEnsureIndexes" value="true"/>
           97         <property name="toEnsureCaps" value="true"/>
           98     </bean>
           99     
          100     <!-- ===============以下是具體DAO的實現===================== -->
          101     
          102     <bean id="hotelDAO" class="me.watchzerg.test.morphia.dao.impl.HotelDAO">
          103         <constructor-arg ref="datastore"/>
          104     </bean>
          105     
          106 </beans> 

          最后寫一個測試類看看我們的成果:

            1 public class MorphiaTest {
            2     private static HotelDAO hotelDAO;
            3 
            4     /**
            5      * 測試Morphia的DAO層
            6      * 
            7      * @param args
            8      * @throws Exception
            9      */
           10     public static void main(String[] args) throws Exception {
           11         // 初始化DAO
           12         initDAO();
           13 
           14         // 插入測試
           15         saveTest();
           16 
           17         // 更新測試
           18         // updateTest();
           19 
           20         // 刪除測試
           21         // deleteTest();
           22 
           23         // 查詢測試
           24         // queryHotel();
           25 
           26         System.out.println("done!");
           27     }
           28 
           29     /**
           30      * 初始化DAO
           31      * <p>
           32      * @Title: initDAO
           33      * </p>
           34      */
           35     private static void initDAO() {
           36         ApplicationContext context = new ClassPathXmlApplicationContext(
           37                 "config.xml");
           38         hotelDAO = (HotelDAO) context.getBean("hotelDAO");
           39     }
           40 
           41     /**
           42      * 生成指定個數的hotelList
           43      * <p>
           44      * @Title: getHotelList
           45      * </p>
           46      * 
           47      * @param num
           48      * @return
           49      */
           50     private static List<Hotel> getHotelList(int num) {
           51         List<Hotel> list = new ArrayList<Hotel>();
           52         for (int i = 0; i < num; i++) {
           53             Hotel hotel = new Hotel();
           54             hotel.setName("編號為[" + i + "]的旅店");
           55             hotel.setStars(i % 10);
           56             Address address = new Address();
           57             address.setCountry("中國");
           58             address.setCity("北京");
           59             address.setStreet("上帝南路");
           60             address.setPostCode("10000" + (i % 10));
           61             hotel.setAddress(address);
           62             list.add(hotel);
           63         }
           64         return list;
           65     }
           66 
           67     /**
           68      * 將hotelList插入數據庫
           69      * <p>
           70      * @Title: saveHotelList
           71      * </p>
           72      * 
           73      * @param hotelDAO
           74      * @param hotelList
           75      */
           76     private static void saveTest() {
           77         List<Hotel> hotelList = getHotelList(100);
           78         for (Hotel hotel : hotelList) {
           79             // Key<Hotel> key=hotelDAO.save(hotel,WriteConcern.SAFE);
           80             Key<Hotel> key = hotelDAO.save(hotel);
           81             System.out.println("id為[" + key.getId() + "]的記錄已被插入");
           82         }
           83     }
           84 
           85     /**
           86      * 更新操作測試
           87      * <p>
           88      * @Title: updateTest
           89      * </p>
           90      * 
           91      * @throws Exception
           92      */
           93     private static void updateTest() throws Exception {
           94         //生成查詢條件
           95         Query<Hotel> q = hotelDAO.createQuery().field("stars")
           96                 .greaterThanOrEq(9);
           97         //生成更新操作
           98         UpdateOperations<Hotel> ops = hotelDAO.createUpdateOperations()
           99                 .set("address.city", "shanghai").inc("stars");
          100         // UpdateResults<Hotel> ur=hotelDAO.update(q, ops);
          101         UpdateResults<Hotel> ur = hotelDAO.updateFirst(q, ops);
          102         if (ur.getHadError()) {
          103             System.out.println(ur.getError());
          104             throw new Exception("更新時發生錯誤");
          105         }
          106         if (ur.getUpdatedExisting()) {
          107             System.out.println("更新成功,更新條數為[" + ur.getUpdatedCount()
          108                     + "],插入條數為[" + ur.getInsertedCount() + "]");
          109         } else {
          110             System.out.println("沒有記錄符合更新條件");
          111         }
          112     }
          113 
          114     /**
          115      * 刪除操作測試
          116      * <p>
          117      * @Title: deleteTest
          118      * </p>
          119      */
          120     private static void deleteTest() {
          121         ObjectId id = hotelDAO.findIds().get(0);
          122         hotelDAO.deleteById(id);
          123 
          124         Query<Hotel> q = hotelDAO.createQuery().field("stars")
          125                 .greaterThanOrEq(100);
          126         hotelDAO.deleteByQuery(q);
          127     }
          128 
          129     /**
          130      * 查詢測試
          131      * <p>
          132      * @Title: queryHotel
          133      * </p>
          134      */
          135     private static void queryHotel() {
          136         // 顯示所有記錄
          137         System.out.println("\nhotelDAO.find()=");
          138         for (Hotel hotel : hotelDAO.find()) {
          139             System.out.println(hotel);
          140         }
          141 
          142         // 統計star大于等于9的數目
          143         System.out
          144                 .println("\nhotelDAO.count(hotelDAO.createQuery().field(\"stars\").greaterThanOrEq(9))="
          145                         + hotelDAO.count(hotelDAO.createQuery().field("stars")
          146                                 .greaterThanOrEq(9)));
          147 
          148         // 顯示符合條件的記錄ID
          149         List<ObjectId> ids = hotelDAO.findIds("stars", 8);
          150         System.out.println("\nhotelDAO.findIds(\"stars\", 8)=");
          151         for (ObjectId id : ids) {
          152             System.out.println(id);
          153         }
          154     }
          155 
          156 }

          大功告成~

          posted on 2012-09-21 18:09 王星游 閱讀(8441) 評論(0)  編輯  收藏 所屬分類: java

          主站蜘蛛池模板: 明水县| 德安县| 田东县| 米易县| 包头市| 铁力市| 上杭县| 淅川县| 阳西县| 封开县| 昌乐县| 长垣县| 莱西市| 阳原县| 布尔津县| 马关县| 祁东县| 建阳市| 额敏县| 宜宾市| 大同市| 海淀区| 临湘市| 喀什市| 仙桃市| 噶尔县| 邯郸市| 金湖县| 汉阴县| 沙湾县| 正定县| 东港市| 崇阳县| 墨玉县| 汽车| 博客| 安西县| 泽库县| 射洪县| 禹城市| 鄄城县|