Spring-data学习笔记.txt 26.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
实现Repository, CrudRepository or PagingAndSortingRepository接口,
或在类上添加@RepositoryDefinition注解,



创建repository 实例:
1.XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/data/jpa"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
  
  <!-- repository-impl-postfix指定repository实现类的名称 == 接口名称 + impl,
  repository-impl-postfix默认值就是impl
  -->
  <repositories base-package="com.acme.repositories" repository-impl-postfix="impl" />

</beans:beans>

2.自定义repository实现Repository,接口或其子接口,放在base-package包下





java代码中获取repository实例对象:(有点类似于ApplicationContext.getBean(...)感觉)
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);




Spring Data 启用 Web支持:
1.采用Java Config方式:
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration  extends WebMvcConfigurationSupport  { }

2.Spring XML配置方式:
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />




Spring Data 默认web支持依赖的是SpringMVC:
congtroller中实现分页查询:
@Controller
@RequestMapping("/users")
public class UserController {

  @Autowired UserRepository repository;

  @RequestMapping
  public String showUsers(Model model, Pageable pageable) {

    model.addAttribute("users", repository.findAll(pageable));
    return "users";
  }
}

page:当前页码,从0开始
page.size:每页显示几条
page.sort:排序字段以及排序方式,默认升序asc
     sort=age,desc
     sort=age,asc
     sort=age,desc&sort=id    //多个排序字段
page.sort.dir


controller中分页:
注入Pageable参数
@RequestMapping
  public String showUsers(Model model, Pageable pageable) {
Pageable对象默认的pageSize即每页显示条数为20,可以通过给Pageable添加@PageableDefaults(value="50")注解来修改





返回JSON数据:
HttpEntity<PagedResources<Person>> persons(Pageable pageable, 
    PagedResourcesAssembler assembler) { 
Page<Person> persons = repository.findAll(pageable); 
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
客户端可能会接受类似这样的JSON数据:
{ "links" : [ { "rel" : "next", 
                "href" : "http://localhost:8080/persons?page=1&size=20 } 
  ], 
  "content" : [ 
     … // 20 Person instances rendered here
  ], 
  "pageMetadata" : { 
    "size" : 20, 
    "totalElements" : 30, 
    "totalPages" : 2, 
    "number" : 0
  } 
}



原始对象到领域模型对象之间的转换器注册:
<mvc:annotation-driven conversion-service="conversionService" />

<bean class="org.springframework.data.repository.support.DomainClassConverter">
  <constructor-arg ref="conversionService" />
</bean>




分页参数解析器注册:
1.Java Config方式:
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(new PageableHandlerArgumentResolver());
  }
}


2.XML配置方式:
<bean class="….web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
  <property name="customArgumentResolvers">
    <list>
      <bean class="org.springframework.data.web.PageableHandlerArgumentResolver" />
    </list>
  </property>
</bean>




spring-data的maven依赖:
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-mongodb</artifactId>
    <version>1.2.4
</version>
  </dependency>

Spring的官方Maven仓库:
<repositories>
  <repository>
    <id>spring-milestone</id>
    <name>Spring Maven MILESTONE Repository</name>
    <url>http://repo.springsource.org/libs-milestone</url>
  </repository>
</repositories>


mongoDB的long4j配置:
log4j.category.org.springframework.data.document.mongodb=DEBUG
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n






Spring-data操作MongoDB的简单示例:
MongoOperations mongoOps = new MongoTemplate(new Mongo(), "your-database-name");

    mongoOps.insert(new Person("Joe", 34));

    log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class));

    mongoOps.dropCollection("person");






Mongo实例注册:
1.Java config方式:
public class AppConfig {
   public @Bean Mongo mongo() throws UnknownHostException {
       return new Mongo(ip, port);
   }
} 

2. Java config方式2:通过Spring的MongoFactoryBean实例获取
public class AppConfig {
     public @Bean MongoFactoryBean mongo() {
          MongoFactoryBean mongo = new MongoFactoryBean();
          mongo.setHost("localhost");
          mongo.setPort(27017);
          return mongo;
     }
}
然后通过工厂bean的getObject()获取Mongo对象

3.SPring XML配置方式:
<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/context
          http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/data/mongo
          http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- Default bean name is 'mongo' -->
    <mongo:mongo host="localhost" port="27017"/>

</beans>
注意:必须引入上面的mongo命名空间
下面是有关mongo数据库相关参数详细配置示例:
 <mongo:mongo host="localhost" port="27017">
    <mongo:options connections-per-host="8"
                   threads-allowed-to-block-for-connection-multiplier="4"
                   connect-timeout="1000"
                   max-wait-time="1500}"
                   auto-connect-retry="true"
                   socket-keep-alive="true"
                   socket-timeout="1500"
                   slave-ok="true"
                   write-number="1"
                   write-timeout="0"
                   write-fsync="true"/>
  </mongo:mongo/>






SimpleMongoDbFactory使用示例:
public class MongoApp {

  private static final Log log = LogFactory.getLog(MongoApp.class);

  public static void main(String[] args) throws Exception {

    MongoOperations mongoOps = new MongoTemplate(new SimpleMongoDbFactory(new Mongo(), "database"));

    mongoOps.insert(new Person("Joe", 34));

    log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class));

    mongoOps.dropCollection("person");
  }
}




MongoDBFactory实例注册:
1. Java config方式:
@Configuration
public class MongoConfiguration {
  
  public @Bean MongoDbFactory mongoDbFactory() throws Exception {
    return new SimpleMongoDbFactory(new Mongo(), "your-database-name");
  }
}

2.Spring-XML配置方式:
<context:property-placeholder location="classpath:/com/myapp/mongodb/config/mongo.properties"/>

<mongo:mongo host="${mongo.host}" port="${mongo.port}">
  <mongo:options
     connections-per-host="${mongo.connectionsPerHost}"
     threads-allowed-to-block-for-connection-multiplier="${mongo.threadsAllowedToBlockForConnectionMultiplier}"
     connect-timeout="${mongo.connectTimeout}"
     max-wait-time="${mongo.maxWaitTime}"
     auto-connect-retry="${mongo.autoConnectRetry}"
     socket-keep-alive="${mongo.socketKeepAlive}"
     socket-timeout="${mongo.socketTimeout}"
     slave-ok="${mongo.slaveOk}"
     write-number="1"
     write-timeout="0"
     write-fsync="true"/>
</mongo:mongo>

<mongo:db-factory dbname="database" mongo-ref="mongo"/>

<bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
  <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>




spring-data的auditing配置:
1.Java Config方式:
@Configuration
@EnableMongoAuditing
class Config {

    @Bean
    public AuditorAware<AuditableUser> myAuditorProvider() {
        return new AuditorAwareImpl();
    }
}

2.Spring XML方式:
<mongo:auditing mapping-context-ref="customMappingContext" auditor-aware-ref="yourAuditorAwareImpl"/>






MongoTemplate模版类是线程安全的,MongoTemplate之于Mongo,类似于JDBCTemplate之于JDBC,都是Spring封装的工具类

MongoTemplate的所有方法抛出的异常都是unchecked异常,即不用强制用户try..catch,Spring做这样处理,是不想让代码被try-catch丑陋化



MongoTemplate实例注册:
1.Java Config方式:
@Configuration
public class AppConfig {

    public @Bean Mongo mongo() throws Exception {
        return new Mongo("localhost");
    }

    public @Bean MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), "mydatabase");
    }
}

2.Spring-XML配置方式:
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongo"/>
    <constructor-arg name="databaseName" value="your-database-name"/>
  </bean>




WriteResultChecking Policy(MongoDB操作返回结果检测策略):
即当mongoDB数据库操作发生异常时,告诉Spring-data该如何处理异常,默认是不处理
WriteResultChecking Policy有如下枚举值:
LOG : 记录日志
EXCEPTION: 抛异常
NONE:什么也不做,默认值为NONE





mongoDB中若你insert一个object时未指定id,则默认生成一个_id Object,类似这样:
{"_id":{"objectId": "xxxxxxxxxxxxxxxxxxxxxxxxxx"}}
假定你有这样一个PO类
public class Person {
    private int id;
    private String name;
}
要实现id属性的映射,spring-data提供了@id注解,注意包路径是:org.springframework.data.annotation.Id
即:
@id
private int id;
默认只要你的PO类中有名称叫id属性就会自动映射到MongoDB的_id属性,如果不是叫id,那就必须用@id注解

如果你的id属性定义的是String类型,那么MappingMongoConverter类型转换器会首先尝试是否能够转换成
MongoDB中的ObjectId对象类型,若不能,则会直接使用String类型存储到MongoDB数据库

如果你的id属性定义的是Integer(或Long)类型,那么MappingMongoConverter类型转换器会首先尝试是否能够转换成MongoDB中的ObjectId对象类型,若不能转换,则MongoDB会自动添加_id属性,且未和PO类中任何属性映射



当你insert一个PO对象时,如果PO对象里有依赖另一个PO对象时,MongoDB会自动帮我们生成一个_class属性,标识出当前的JSON Object在Java中的class类型,用例子说明可能比较形象:
public class Sample {
  Contact value;
}

public abstract class Contact { … }

public class Person extends Contact { 
    private int id;
    private String name;
}

Sample sample = new Sample();
Person person = new Person();
person.setId(1);
person.setName("John");
sample.value = person;

mongoTemplate.save(sample);

{ "_class" : "com.acme.Sample",
  "value" : { "_class" : "com.acme.Person","_id": {"objectId" : "1"},"name" : "john" }
}

默认MongoDB帮我们生成的_class属性值是java类的完整包路径,比如: com.xx.oo.Person
如果你想改变这种默认行为,你可以使用@TypeAlias注解
@TypeAlias("per")
class Person {
......
}
这样生成的_class属性可能是这样:
{"_class" : "per"}




Java对象里属性的数据类型和MongoDB里的数据类型之间的映射关系是通过MongoTypeMapper绑定的,
如果你自定义的Java对象类型不在 MongoTypeMapper 支持范围内,你可能需要实现此接口自己实现两种
类型之间的映射:
然后在Spring XML中注册你自己的类型映射器:
<mongo:mapping-converter type-mapper-ref="your-MongoTypeMapper-bean-name"/>

<bean name="your-MongoTypeMapper-bean-name" class="your-MongoTypeMapper-class"/>



MongoDB提供了insert和save两种保存java 对象到MongoDB数据库的方法:
Person p = new Person("Bob", 33);
mongoTemplate.insert(p);
insert和save的细微区别:
1. 当person对象没有指定id属性,则两者执行的操作没有区别,都是插入一条记录到MongDB的collection中,
那默认插到哪个Collection里呢,Spring-data默认插到你的Java类名首字母小写的collection中,
比如上面的mongoTemplate.insert(p);,那么实际默认就是插入到person中,我们知道MongoDB是会自动创建
collection的,当然spring-data的MongoDBTemplate类的insert也提供了insert(class,collectionName)的重载
第二个参数即满足你需要手工指定collection名称的需求,当然save也有同样的重载
2.当person对象指定了id属性,即person.setId(1);
那么insert会首先检查数据库是否存在相同id的数据,若存在则会抛异常
而save则会对id相同的数据执行更新操作



关于Spring-data提供的更新方法对MongoDB的 update的支持:
Spring-data的Update提供了如下方法来支持MongoDB的各种update selector(更新操作符)
对MongoDB的原生更新操作符不熟悉的,请参阅我写的MongoDB的常用命令.docx文档,里面有详细介绍,
spring-data只不过是用java对这些操作符进行封装下,其实最终还是要生成你在cmd命令窗口敲的那么命令,
在Java里我们当前希望是以面向对象的方式来操作MongoDB,
看到Update类里提供的那些方法的名字就一目了然是对应到MongoDB中哪个update selector:
updateFirst 更新符合匹配的第一个
updateMulti 更新符合匹配的多个
Update addToSet (String key, Object value)

Update inc (String key, Number inc)

Update pop (String key, Update.Position pos) 

Update pull (String key, Object value)

Update pullAll (String key, Object[] values)

Update push (String key, Object value)

Update pushAll (String key, Object[] values) 
太多了我就不一一贴了,什么意思干什么用的,只要你熟悉了Mon个DB的原生更新操作符就一目了然咯





介绍下MongoDBTemplate提供的findAndModify方法对MongoDB原生的findAndModify函数的支持:
findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass, String collectionName);
参数解释:
query: 过滤参数,类似于SQL里的where条件,这里可以用spring-date提供的Where对象以OO的方式来构建query表达式,可以直接使用JSON字符串的方式构建,如果你习惯了cmd下敲命令,那么这种以字符串方式构建查询参数可能比较适合你.示例如下:
BasicQuery query = new BasicQuery("{ age : { $lt : 50 }, accounts.balance : { $gt : 1000.00 }}");
List<Person> result = mongoTemplate.find(query, Person.class); 

udate:即更新参数,如:
Update update = new Update().inc("age", 1); //表示age属性值加1
options:对findAndmodify行为参数设置,对应到MongoDB原生的findAndModify函数的new和remove,upsert等参数,如:
new FindAndModifyOptions().returnNew(true)  //即表示new参数设置为true,new参数为true表示执行findAndModify返回更新后的新对象,默认为false返回的是更新前的旧对象
new FindAndModifyOptions().returnNew(true).upsert(true) //upsert参数设置为true表示当根据第一个query参数没有找到数据时,需要把第二个update参数对象插入到collection中,默认为false
new FindAndModifyOptions().remove(true)  //即表示对根据第一个query参数匹配到的数据执行remove操作,即类似SQL里的delete from table where xxx,默认remove参数为false,
总结:findModify方法包含了更新,删除,插入等操作,到底执行哪个操作,由FindAndModifyOptions参数配置决定
entityClass:表示返回的DBObject应该映射到哪个Java Object
collectionName:即告诉Spring-data你要操作哪个collection








Spring data对MongoDB的map-reduce(类似于SQL里的存储过程)的支持:
mongoDB的mapReduce命令示例:
{ "_id" : ObjectId("4e5ff893c0277826074ec533"), "x" : [ "a", "b" ] }
{ "_id" : ObjectId("4e5ff893c0277826074ec534"), "x" : [ "b", "c" ] }
{ "_id" : ObjectId("4e5ff893c0277826074ec535"), "x" : [ "c", "d" ] }


db.orders.mapReduce(
    function () {
        for (var i = 0; i < this.x.length; i++) {
            emit(this.x[i], 1);
        }
    },
    function (key, values) {
    var sum = 0;
    for (var i = 0; i < values.length; i++)
        sum += values[i];
    return sum;
    }
);
返回:
{ "_id" : "a", "value" : 1 }
{ "_id" : "b", "value" : 2 }
{ "_id" : "c", "value" : 2 }
{ "_id" : "d", "value" : 1 }

Spring-data API操作方式:
MapReduceResults<ValueObject> results = mongoTemplate.mapReduce("your-collection-name", "classpath:map.js", "classpath:reduce.js", ValueObject.class);
for (ValueObject valueObject : results) {
  System.out.println(valueObject);
}
打印如下:
ValueObject [id=a, value=1.0]
ValueObject [id=b, value=2.0]
ValueObject [id=c, value=2.0]
ValueObject [id=d, value=1.0]

ValueObject是一个普通的JavaBean
public class ValueObject {
  private String id;
  private float value;

MongoTemplate的mapReduce方法参数说明:
第一个参数是你要操作的collection名称,
第二个参数是要执行的function,可以直接用字符串指定js的function,也可以通过classpath指定类路径下的一个js文件,第三个参数跟第二个参数类似,分别对应MongoDB中原生的mapReduce命令的两个function参数,
最后一个class类型表示数据库返回的DBObject类型自动转成java里的什么类型



MongoTemplated对MongoDB原生的group函数支持:
GroupByResults<XObject> results = mongoTemplate.group("your_collection-name", 
                                                      GroupBy.key("分组属性").initialDocument("{ count: 0 }").reduceFunction("function(doc, prev) { prev.count += 1 }"), Your-XXOO-Object.class);
返回的JSON数据类似这样
{ 
  "retval" : [ { "x" : 1.0 , "count" : 2.0} , 
               { "x" : 2.0 , "count" : 1.0} , 
               { "x" : 3.0 , "count" : 3.0} ] , 
  "count" : 6.0 , 
  "keys" : 3 , 
  "ok" : 1.0
}
同理reduceFunction的参数也可以通过classpath引入一个外部的js文件,如:classpath:com/xx/oo/abc.js
即表示reduceFunction里的function参数从外部的js文件读取,当你的function内容很长,在程序里拼接显得不利于以后维护,这是外部引入就觉得尤为重要.





Spring-data对MongoDB的aggregate函数支持:
Aggregation aggregation = newAggregation(
    pipelineOP1(),
    pipelineOP2(),
    pipelineOPn()
);

AggregationResults<OutputType> results = mongoTemplate.aggregate(aggregation,"your-collection-name", XXOO.class);
List<OutputType> mappedResult = results.getMappedResults();
Aggregation类的具体构建请参看API文档




Spring-data对$Projection 表达式的支持:
project("name", "netPrice")             // {$project: {name: 1, netPrice: 1}}
project().and("foo").as("bar")         // {$project: {bar: $foo}}
project("a","b").and("foo").as("bar") // {$project: {a: 1, b: 1, bar: $foo}}






下面是官方提供的6个有关Aggregate聚合统计方面的示例:
class TagCount {
 String tag;
 int n;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

Aggregation agg = newAggregation(
    project("tags"),
    unwind("tags"),
    group("tags").count().as("n"),
    project("n").and("tag").previousOperation(),
    sort(DESC, "n") 
);

AggregationResults<TagCount> results = mongoTemplate.aggregate(agg, "tags", TagCount.class);
List<TagCount> tagCount = results.getMappedResults();

********************************华丽的分割线**************************************************
class ZipInfo {
   String id;
   String city;
   String state;
   @Field("pop") int population;
   @Field("loc") double[] location;
}

class City {
   String name;
   int population;
}

class ZipInfoStats {
   String id;
   String state;
   City biggestCity;
   City smallestCity;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class,
    group("state", "city")
       .sum("population").as("pop"),
    sort(ASC, "pop", "state", "city"),
    group("state")
       .last("city").as("biggestCity")
       .last("pop").as("biggestPop")
       .first("city").as("smallestCity")
       .first("pop").as("smallestPop"),
    project()
       .and("state").previousOperation()
       .and("biggestCity")
          .nested(bind("name", "biggestCity").and("population", "biggestPop"))
       .and("smallestCity")
          .nested(bind("name", "smallestCity").and("population", "smallestPop")),
    sort(ASC, "state")
);

AggregationResults<ZipInfoStats> result = mongoTemplate.aggregate(aggregation, ZipInfoStats.class);
ZipInfoStats firstZipInfoStats = result.getMappedResults().get(0);


********************************华丽的分割线**************************************************

class StateStats {
   @Id String id;
   String state;
   @Field("totalPop") int totalPopulation;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<ZipInfo> agg = newAggregation(ZipInfo.class,
    group("state").sum("population").as("totalPop"),
    sort(ASC, previousOperation(), "totalPop"),
    match(where("totalPop").gte(10 * 1000 * 1000))
);

AggregationResults<StateStats> result = mongoTemplate.aggregate(agg, StateStats.class);
List<StateStats> stateStatsList = result.getMappedResults();


********************************华丽的分割线**************************************************

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .and("netPrice").plus(1).as("netPricePlus1")
        .and("netPrice").minus(1).as("netPriceMinus1")
        .and("netPrice").multiply(1.19).as("grossPrice")
        .and("netPrice").divide(2).as("netPriceDiv2")
        .and("spaceUnits").mod(2).as("spaceUnitsMod2")
);

AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();

********************************华丽的分割线**************************************************

class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .andExpression("netPrice + 1").as("netPricePlus1")
        .andExpression("netPrice - 1").as("netPriceMinus1")
        .andExpression("netPrice / 2").as("netPriceDiv2")
        .andExpression("netPrice * 1.19").as("grossPrice")
        .andExpression("spaceUnits % 2").as("spaceUnitsMod2")
        .andExpression("(netPrice * 0.8  + 1.2) * 1.19").as("grossPriceIncludingDiscountAndCharge")

);

AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();

********************************华丽的分割线**************************************************


class Product {
    String id;
    String name;
    double netPrice;
    int spaceUnits;
}
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

double shippingCosts = 1.2;

TypedAggregation<Product> agg = newAggregation(Product.class,
    project("name", "netPrice")
        .andExpression("(netPrice * (1-discountRate)  + [0]) * (1+taxRate)", shippingCosts).as("salesPrice")
);

AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();







自定义DBObject与自定义PO之间的类型转换器:
public class PersonWriteConverter implements Converter<Person, DBObject> {

  public DBObject convert(Person source) {
    DBObject dbo = new BasicDBObject();
    dbo.put("_id", source.getId());
    dbo.put("name", source.getFirstName());
    dbo.put("age", source.getAge());
    return dbo;
  }
}
自定义转换器注册:
<mongo:mapping-converter>
  <mongo:custom-converters>
    <mongo:converter ref="writeConverter"/>
  </mongo:custom-converters>
</mongo:mapping-converter>




为collection创建索引:
mongoTemplate.indexOps(Person.class).ensureIndex(new Index().on("name",Order.ASCENDING));   


查询索引:
template.indexOps(Person.class).ensureIndex(new Index().on("age", Order.DESCENDING).unique(Duplicates.DROP));
List<IndexInfo> indexInfoList = template.indexOps(Person.class).getIndexInfo();
Duplicates:表示重复索引的处理策略,drop表示重复的删除



MongoTemplate也封装了MongoDB-java驱动中Mongo.runCommand()方法:
CommandResult executeCommand (DBObject command)

CommandResult executeCommand (String jsonCommand)



Spring-data为Mongo的生命周期设计了事件监听器:AbstractMongoEventListener:
可以在mongo的每个生命周期开始或结束时刻,动态嵌入一段代码:
事件有:
onBeforeConvert    java对象转成成MongoDB中的DBObject之前
onBeforeSave       在insert之前
onAfterSave        insert之后
onAfterConvert     java对象转成成MongoDB中的DBObject之后










Spring配置文件中注册MongoTemplate:
<?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:mongo="http://www.springframework.org/schema/data/mongo"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/data/mongo
    http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">

  <mongo:mongo id="mongo" />
  
  <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongo" />
    <constructor-arg value="databaseName" />
  </bean>

  <mongo:repositories base-package="com.acme.*.repositories" />

</beans>
注意:mongo命名空间必须要引入




用方法名语义来代替接口实现:
GreaterThan                            findByAgeGreaterThan(int age) {"age" : {"$gt" : age}} 
LessThan                               findByAgeLessThan(int age) {"age" : {"$lt" : age}} 
Between                                findByAgeBetween(int from, int to) {"age" : {"$gt" : from, "$lt" : to}} 
IsNotNull, NotNull                     findByFirstnameNotNull() {"age" : {"$ne" : null}} 
IsNull, Null                           findByFirstnameNull() {"age" : null} 
Like                                   findByFirstnameLike(String name) {"age" : age} ( age as regex) 
Regex                                  findByFirstnameRegex(String firstname) {"firstname" : {"$regex" : firstname }} 
(No keyword)                           findByFirstname(String name) {"age" : name} 
Not                                    findByFirstnameNot(String name) {"age" : {"$ne" : name}} 
Near                                   findByLocationNear(Point point) {"location" : {"$near" : [x,y]}} 
Within                                 findByLocationWithin(Circle circle) {"location" : {"$within" : {"$center" : [ [x, y], distance]}}} 
Within                                 findByLocationWithin(Box box) {"location" : {"$within" : {"$box" : [ [x1, y1], x2, y2]}}}True 
IsTrue, True                           findByActiveIsTrue() {"active" : true} 
IsFalse, False                         findByActiveIsFalse() {"active" : false} 
Exists                                 findByLocationExists(boolean exists) {"location" : {"$exists" : exists }} 








Spring-data对Log4J的支持:
配置样例:
log4j.rootCategory=INFO, stdout

log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.stdout.host = localhost 
log4j.appender.stdout.port = 27017
log4j.appender.stdout.database = logs
log4j.appender.stdout.collectionPattern = %X{year}%X{month}
log4j.appender.stdout.applicationId = my.application
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE

log4j.category.org.apache.activemq=ERROR
log4j.category.org.springframework.batch=DEBUG
log4j.category.org.springframework.data.document.mongodb=DEBUG
log4j.category.org.springframework.transaction=INFO