1. 程式人生 > >MongoDB自動增長id實現、自定義函式呼叫、與Spring整合

MongoDB自動增長id實現、自定義函式呼叫、與Spring整合

昨天同事問實現MongoDB主鍵自動增長有什麼好的辦法,雖然喜歡MongoDB客戶端驅動程式自動生成的id,不過還是來測試了一下,僅僅是測試哦

廢話少說微笑

1、建立專案,新增依賴

    <dependencies>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
            <version>1.7.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.1.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </dependency>
    </dependencies>

2、使用了spring-data-mongo,所以建立封裝資料的演示類

public class User {

    private long id;

    private String name;

    public User(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}</span>
3、建立資源配置檔案app.properties
mongodb.host=localhost
mongodb.port=27017

4、spring配置檔案

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mongo="http://www.springframework.org/schema/data/mongo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">

    <context:property-placeholder location="classpath:app.properties"/>

    <mongo:mongo-client id="mongoClient" host="${mongodb.host}" port="${mongodb.port}"/>

    <mongo:db-factory id="dbFactory" dbname="script" mongo-ref="mongoClient"/>

    <mongo:template id="mongoTemplate" db-factory-ref="dbFactory" converter-ref="mappingMongoConverter"/>

    <bean id="dbRefResolver" class="org.springframework.data.mongodb.core.convert.DefaultDbRefResolver">
        <constructor-arg ref="dbFactory"/>
    </bean>

    <bean id="mongoMappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext"/>

    <bean id="defaultMongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
        <constructor-arg name="typeKey">
            <null/>
        </constructor-arg>
    </bean>

    <bean id="mappingMongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
        <constructor-arg name="dbRefResolver" ref="dbRefResolver"/>
        <constructor-arg name="mappingContext" ref="mongoMappingContext"/>
        <property name="typeMapper" ref="defaultMongoTypeMapper"/>
    </bean>

</beans>

5、測試程式碼

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.ScriptOperations;
import org.springframework.data.mongodb.core.script.ExecutableMongoScript;
import org.springframework.data.mongodb.core.script.NamedMongoScript;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
import static org.springframework.data.mongodb.core.query.Update.update;

/**
 * (1) Test an auto-increment pattern for the _id field.
 *
 * (2) Test MongoDB Script and Spring Script Operations.
 *
 * Created by gaofu on 16-2-5.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
public class AutoIncrementIdAndScriptTest {

    @Autowired
    private MongoTemplate template;

    @Before
    public void setUp() throws Exception {
        ScriptOperations scriptOps = template.scriptOps();

        // 將JavaScript函式儲存到MongoDB server,以便直接在客戶端呼叫
        scriptOps.register(new NamedMongoScript("getNextSequence", "function(name){var ret=db.counters.findAndModify({query:{_id:name},update:{$inc:{seq:NumberLong(1)}},new:true});return ret.seq.floatApprox}"));
//        scriptOps.register(new NamedMongoScript("getNextSequence", "function(name){var ret=db.counters.findAndModify({query:{_id:name},update:{$inc:{seq:1}},new:true});return ret.seq}")); // 這樣就會把seq變成雙精度浮點型

        template.upsert(query(where("_id").is("userid")), update("seq", 0L), "counters");
    }


    @Test
    public void testClientScript() {
        final String origin = "Hello World";
        String script = "function(x){return x + \".\"}";

        ExecutableMongoScript mongoScript = new ExecutableMongoScript(script);

        ScriptOperations scriptOps = template.scriptOps();

        Object result1 = scriptOps.execute(mongoScript, origin);
        // Spring使用String.format()方法對字串進行了處理
//        System.out.println(result1);
        assertEquals(String.format("'%s'", origin) + '.', result1);

        Object mongoEval = template.getDb().eval(script, origin);
//        System.out.println(mongoEval);
        assertEquals(origin + ".", mongoEval);

        Object result2 = scriptOps.execute(mongoScript, 3);
        assertEquals("3.", result2);
    }
    @Test
    public void testAutoIncrementIdAndStoredScript() {
        ScriptOperations scriptOps = template.scriptOps();

        boolean exists = scriptOps.exists("getNextSequence");
        assertTrue(exists);

        // JavaScript返回的總是雙精度浮點型數字,所以需要轉換
        User jack = new User(((Number) scriptOps.call("getNextSequence", "userid")).longValue(), "Jack");
        User rose = new User(((Number) scriptOps.call("getNextSequence", "userid")).longValue(), "Rose");
        template.insert(jack);
        template.insert(rose);

        assertEquals(1, jack.getId());
        assertEquals(2, rose.getId());

        DB db = template.getDb();
        Object eval = db.eval("getNextSequence('userid')");
        // JavaScript返回的總是雙精度浮點型數字
        assertEquals(3.0d, eval);
    }


    /**
     * 註釋掉此方法可以檢視資料庫中的集合資料。
     */
    @After
    public void tearDown() throws Exception {
        template.dropCollection("counters");
        template.dropCollection(User.class);
        template.getCollection("system.js").remove(new BasicDBObject("_id", "getNextSequence"));
    }
}


6、spring需要日誌輸出,簡單配置如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration>
    <appender name="default" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %40.40c:%4L - %m%n"/>
        </layout>
    </appender>

    <root>
        <level value="info"/>
        <appender-ref ref="default"/>
    </root>
</log4j:configuration>

備註:

(1)別忘了在本地啟動MongoDB伺服器。

(2)如果比較懶,想親自動手測試,又懶得拷貝程式碼,可以下載原始碼