First stab at mapping

This commit is contained in:
J. Brisbin
2011-03-07 15:35:30 -06:00
parent 6f12450be6
commit 15768b3a44
85 changed files with 6997 additions and 1571 deletions

View File

@@ -1,132 +1,136 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-document-parent</artifactId> <artifactId>spring-data-document-parent</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version> <version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-data-document-parent/pom.xml</relativePath> <relativePath>../spring-data-document-parent/pom.xml</relativePath>
</parent> </parent>
<artifactId>spring-data-document-core</artifactId> <artifactId>spring-data-document-core</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Spring Data Document Support</name> <name>Spring Data Document Support</name>
<dependencies> <dependencies>
<!-- Spring --> <!-- Spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId> <artifactId>spring-beans</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId> <artifactId>spring-tx</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- Logging --> <!-- Logging -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId> <artifactId>jcl-over-slf4j</artifactId>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId> <artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>javax.mail</groupId> <groupId>javax.mail</groupId>
<artifactId>mail</artifactId> <artifactId>mail</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>javax.jms</groupId> <groupId>javax.jms</groupId>
<artifactId>jms</artifactId> <artifactId>jms</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>com.sun.jdmk</groupId> <groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId> <artifactId>jmxtools</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>com.sun.jmx</groupId> <groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId> <artifactId>jmxri</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>javax.annotation</groupId>
<artifactId>mockito-all</artifactId> <artifactId>jsr250-api</artifactId>
<scope>test</scope> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>org.mockito</groupId>
<artifactId>junit</artifactId> <artifactId>mockito-all</artifactId>
</dependency> <scope>test</scope>
</dependency>
<!-- Dependencies for web analytics functionality - to me moved into spring framework --> <dependency>
<dependency> <groupId>junit</groupId>
<groupId>javax.servlet</groupId> <artifactId>junit</artifactId>
<artifactId>servlet-api</artifactId> </dependency>
<version>2.5</version>
<scope>provided</scope>
</dependency> <!-- Dependencies for web analytics functionality - to me moved into spring framework -->
<!-- Spring dependencies --> <dependency>
<dependency> <groupId>javax.servlet</groupId>
<groupId>org.springframework</groupId> <artifactId>servlet-api</artifactId>
<artifactId>spring-core</artifactId> <version>2.5</version>
<version>${org.springframework.version}</version> <scope>provided</scope>
<exclusions> </dependency>
<exclusion> <!-- Spring dependencies -->
<groupId>commons-logging</groupId> <dependency>
<artifactId>commons-logging</artifactId> <groupId>org.springframework</groupId>
</exclusion> <artifactId>spring-core</artifactId>
</exclusions> <version>${org.springframework.version}</version>
</dependency> <exclusions>
<dependency> <exclusion>
<groupId>org.springframework</groupId> <groupId>commons-logging</groupId>
<artifactId>spring-web</artifactId> <artifactId>commons-logging</artifactId>
<version>${org.springframework.version}</version> </exclusion>
<exclusions> </exclusions>
<exclusion> </dependency>
<groupId>commons-logging</groupId> <dependency>
<artifactId>commons-logging</artifactId> <groupId>org.springframework</groupId>
</exclusion> <artifactId>spring-web</artifactId>
</exclusions> <version>${org.springframework.version}</version>
</dependency> <exclusions>
<dependency> <exclusion>
<groupId>org.springframework</groupId> <groupId>commons-logging</groupId>
<artifactId>spring-webmvc</artifactId> <artifactId>commons-logging</artifactId>
<version>${org.springframework.version}</version> </exclusion>
<exclusions> </exclusions>
<exclusion> </dependency>
<groupId>commons-logging</groupId> <dependency>
<artifactId>commons-logging</artifactId> <groupId>org.springframework</groupId>
</exclusion> <artifactId>spring-webmvc</artifactId>
</exclusions> <version>${org.springframework.version}</version>
</dependency> <exclusions>
</dependencies> <exclusion>
<build> <groupId>commons-logging</groupId>
<plugins> <artifactId>commons-logging</artifactId>
<plugin> </exclusion>
<groupId>com.springsource.bundlor</groupId> </exclusions>
<artifactId>com.springsource.bundlor.maven</artifactId> </dependency>
</plugin> </dependencies>
</plugins> <build>
</build> <plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
</project> </project>

View File

@@ -24,9 +24,15 @@ Import-Template:
org.springframework.core.*;version="[3.0.0, 4.0.0)", org.springframework.core.*;version="[3.0.0, 4.0.0)",
org.springframework.dao.*;version="[3.0.0, 4.0.0)", org.springframework.dao.*;version="[3.0.0, 4.0.0)",
org.springframework.util.*;version="[3.0.0, 4.0.0)", org.springframework.util.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.common.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.spel.standard.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.spel.support.*;version="[3.0.0, 4.0.0)",
org.springframework.validation.*;version="[3.0.0, 4.0.0)",
org.springframework.data.core.*;version="[1.0.0, 2.0.0)", org.springframework.data.core.*;version="[1.0.0, 2.0.0)",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)", org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0" org.w3c.dom.*;version="0",
javax.persistence.*;version="[1.0, 2.0)"

View File

@@ -1,400 +1,407 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<modelVersion>4.0.0</modelVersion> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<groupId>org.springframework.data</groupId> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-document-parent</artifactId> <groupId>org.springframework.data</groupId>
<name>Spring Data Document Parent</name> <artifactId>spring-data-document-parent</artifactId>
<url>http://www.springsource.org/spring-data/data-document</url> <name>Spring Data Document Parent</name>
<version>1.0.0.BUILD-SNAPSHOT</version> <url>http://www.springsource.org/spring-data/data-document</url>
<packaging>pom</packaging> <version>1.0.0.BUILD-SNAPSHOT</version>
<properties> <packaging>pom</packaging>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <properties>
<!-- versions for commonly-used dependencies --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.8.1</junit.version> <!-- versions for commonly-used dependencies -->
<log4j.version>1.2.15</log4j.version> <junit.version>4.8.1</junit.version>
<org.mockito.version>1.8.4</org.mockito.version> <log4j.version>1.2.15</log4j.version>
<org.slf4j.version>1.5.10</org.slf4j.version> <org.mockito.version>1.8.4</org.mockito.version>
<org.springframework.version>3.0.5.RELEASE</org.springframework.version> <org.slf4j.version>1.5.10</org.slf4j.version>
<data.commons.version>1.0.0.BUILD-SNAPSHOT</data.commons.version> <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
<aspectj.version>1.6.11.M2</aspectj.version> <data.commons.version>1.0.0.BUILD-SNAPSHOT</data.commons.version>
</properties> <aspectj.version>1.6.11.M2</aspectj.version>
<profiles> </properties>
<profile> <profiles>
<id>strict</id> <profile>
<properties> <id>strict</id>
<maven.test.failure.ignore>false</maven.test.failure.ignore> <properties>
</properties> <maven.test.failure.ignore>false</maven.test.failure.ignore>
</profile> </properties>
<profile> </profile>
<id>fast</id> <profile>
<properties> <id>fast</id>
<maven.test.skip>true</maven.test.skip> <properties>
<maven.javadoc.skip>true</maven.javadoc.skip> <maven.test.skip>true</maven.test.skip>
</properties> <maven.javadoc.skip>true</maven.javadoc.skip>
</profile> </properties>
<profile> </profile>
<id>staging</id> <profile>
<distributionManagement> <id>staging</id>
<site> <distributionManagement>
<id>spring-site-staging</id> <site>
<url>file:///${java.io.tmpdir}/spring-data/data-document/docs</url> <id>spring-site-staging</id>
</site> <url>file:///${java.io.tmpdir}/spring-data/data-document/docs</url>
<repository> </site>
<id>spring-milestone-staging</id> <repository>
<url>file:///${java.io.tmpdir}/spring-data/data-document/milestone</url> <id>spring-milestone-staging</id>
</repository> <url>file:///${java.io.tmpdir}/spring-data/data-document/milestone</url>
<snapshotRepository> </repository>
<id>spring-snapshot-staging</id> <snapshotRepository>
<url>file:///${java.io.tmpdir}/spring-data/data-document/snapshot</url> <id>spring-snapshot-staging</id>
</snapshotRepository> <url>file:///${java.io.tmpdir}/spring-data/data-document/snapshot</url>
</distributionManagement> </snapshotRepository>
</profile> </distributionManagement>
<profile> </profile>
<id>bootstrap</id> <profile>
<!-- TODO: move the repositories in here before release --> <id>bootstrap</id>
</profile> <!-- TODO: move the repositories in here before release -->
</profiles> </profile>
<distributionManagement> </profiles>
<!-- see 'staging' profile for dry-run deployment settings --> <distributionManagement>
<downloadUrl>http://www.springsource.com/download/community <!-- see 'staging' profile for dry-run deployment settings -->
</downloadUrl> <downloadUrl>http://www.springsource.com/download/community
<site> </downloadUrl>
<id>static.springframework.org</id> <site>
<url>scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-document/docs/${project.version} <id>static.springframework.org</id>
</url> <url>
</site> scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-document/docs/${project.version}
<repository> </url>
<id>spring-milestone</id> </site>
<name>Spring Milestone Repository</name> <repository>
<url>s3://maven.springframework.org/milestone</url> <id>spring-milestone</id>
</repository> <name>Spring Milestone Repository</name>
<snapshotRepository> <url>s3://maven.springframework.org/milestone</url>
<id>spring-snapshot</id> </repository>
<name>Spring Snapshot Repository</name> <snapshotRepository>
<url>s3://maven.springframework.org/snapshot</url> <id>spring-snapshot</id>
</snapshotRepository> <name>Spring Snapshot Repository</name>
</distributionManagement> <url>s3://maven.springframework.org/snapshot</url>
<dependencyManagement> </snapshotRepository>
<!-- </distributionManagement>
inheritable <dependency> declarations for child poms. children still <dependencyManagement>
must explicitly declare the groupId/artifactId of these dependencies <!--
in order for them to show up on the classpath, but metadata like inheritable <dependency> declarations for child poms. children still
<version> and <scope> are inherited, which cuts down on verbosity. must explicitly declare the groupId/artifactId of these dependencies
see in order for them to show up on the classpath, but metadata like
http://www.sonatype.com/books/mvnref-book/reference/pom-relationships-sect-dep-manage.html <version> and <scope> are inherited, which cuts down on verbosity.
--> see
<dependencies> http://www.sonatype.com/books/mvnref-book/reference/pom-relationships-sect-dep-manage.html
-->
<dependencies>
<!-- Spring --> <!-- Spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId> <artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId> <artifactId>spring-beans</artifactId>
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>commons-logging</groupId> <groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId> <artifactId>commons-logging</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId> <artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId> <artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId> <artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
<scope>test</scope> </dependency>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring Data --> <!-- Spring Data -->
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-core</artifactId> <artifactId>spring-data-commons-core</artifactId>
<version>${data.commons.version}</version> <version>${data.commons.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons-aspects</artifactId> <artifactId>spring-data-commons-aspects</artifactId>
<version>${data.commons.version}</version> <version>${data.commons.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-document-core</artifactId> <artifactId>spring-data-couchdb</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchdb</artifactId> <artifactId>spring-data-mongodb</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Logging --> <!-- Logging -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version> <version>${org.slf4j.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId> <artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j.version}</version> <version>${org.slf4j.version}</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId> <artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version> <version>${org.slf4j.version}</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
<version>${log4j.version}</version> <version>${log4j.version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>javax.mail</groupId> <groupId>javax.mail</groupId>
<artifactId>mail</artifactId> <artifactId>mail</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>javax.jms</groupId> <groupId>javax.jms</groupId>
<artifactId>jms</artifactId> <artifactId>jms</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>com.sun.jdmk</groupId> <groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId> <artifactId>jmxtools</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>com.sun.jmx</groupId> <groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId> <artifactId>jmxri</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.annotation</groupId> <groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId> <artifactId>jsr250-api</artifactId>
<version>1.0</version> <version>1.0</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId> <artifactId>mockito-all</artifactId>
<version>${org.mockito.version}</version> <version>${org.mockito.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>${junit.version}</version> <version>${junit.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<dependencies> <dependencies>
<!-- <!--
dependency definitions to be inherited by child poms. any dependency definitions to be inherited by child poms. any
<dependency> declarations here will automatically show up on child <dependency> declarations here will automatically show up on child
project classpaths. only items that are truly common across all project classpaths. only items that are truly common across all
projects (modules and samples) should go here. otherwise, consider projects (modules and samples) should go here. otherwise, consider
<dependencyManagement> above <dependencyManagement> above
--> -->
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
<version>${log4j.version}</version> <version>${log4j.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<extensions> <extensions>
<extension> <extension>
<!-- <!--
available only in the springframework maven repository. see available only in the springframework maven repository. see
<repositories> section below <repositories> section below
--> -->
<groupId>org.springframework.build.aws</groupId> <groupId>org.springframework.build.aws</groupId>
<artifactId>org.springframework.build.aws.maven</artifactId> <artifactId>org.springframework.build.aws.maven</artifactId>
<version>3.1.0.RELEASE</version> <version>3.1.0.RELEASE</version>
</extension> </extension>
</extensions> </extensions>
<resources> <resources>
<resource> <resource>
<directory>${project.basedir}/src/main/java</directory> <directory>${project.basedir}/src/main/java</directory>
<includes> <includes>
<include>**/*</include> <include>**/*</include>
</includes> </includes>
<excludes> <excludes>
<exclude>**/*.java</exclude> <exclude>**/*.java</exclude>
</excludes> </excludes>
</resource> </resource>
<resource> <resource>
<directory>${project.basedir}/src/main/resources</directory> <directory>${project.basedir}/src/main/resources</directory>
<includes> <includes>
<include>**/*</include> <include>**/*</include>
</includes> </includes>
</resource> </resource>
</resources> </resources>
<testResources> <testResources>
<testResource> <testResource>
<directory>${project.basedir}/src/test/java</directory> <directory>${project.basedir}/src/test/java</directory>
<includes> <includes>
<include>**/*</include> <include>**/*</include>
</includes> </includes>
<excludes> <excludes>
<exclude>**/*.java</exclude> <exclude>**/*.java</exclude>
</excludes> </excludes>
</testResource> </testResource>
<testResource> <testResource>
<directory>${project.basedir}/src/test/resources</directory> <directory>${project.basedir}/src/test/resources</directory>
<includes> <includes>
<include>**/*</include> <include>**/*</include>
</includes> </includes>
<excludes> <excludes>
<exclude>**/*.java</exclude> <exclude>**/*.java</exclude>
</excludes> </excludes>
</testResource> </testResource>
</testResources> </testResources>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <configuration>
<source>1.5</source> <source>1.5</source>
<target>1.5</target> <target>1.5</target>
<compilerArgument>-Xlint:all</compilerArgument> <compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings> <showWarnings>true</showWarnings>
<showDeprecation>false</showDeprecation> <showDeprecation>false</showDeprecation>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<useFile>false</useFile> <useFile>false</useFile>
<includes> <includes>
<include>**/*Tests.java</include> <include>**/*Tests.java</include>
</includes> </includes>
<excludes> <excludes>
<exclude>**/Abstract*.java</exclude> <exclude>**/Abstract*.java</exclude>
</excludes> </excludes>
<junitArtifactName>junit:junit</junitArtifactName> <junitArtifactName>junit:junit</junitArtifactName>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<executions> <executions>
<execution> <execution>
<id>attach-sources</id> <id>attach-sources</id>
<goals> <goals>
<goal>jar</goal> <goal>jar</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<!-- <!--
configures the springsource bundlor plugin, which generates configures the springsource bundlor plugin, which generates
OSGI-compatible MANIFEST.MF files during the 'compile' phase of OSGI-compatible MANIFEST.MF files during the 'compile' phase of
the maven build. this plugin is declared within the the maven build. this plugin is declared within the
pluginManagement section because not every module that inherits pluginManagement section because not every module that inherits
from this pom needs bundlor's services, e.g.: from this pom needs bundlor's services, e.g.:
spring-integration-samples and all its children. for this reason, spring-integration-samples and all its children. for this reason,
all modules that wish to use bundlor must declare it explicitly. all modules that wish to use bundlor must declare it explicitly.
it is not necessary to specify the <version> or <configuration> it is not necessary to specify the <version> or <configuration>
sections, but groupId and artifactId are required. see sections, but groupId and artifactId are required. see
http://static.springsource.org/s2-bundlor/1.0.x/user-guide/html/ch04s03.html http://static.springsource.org/s2-bundlor/1.0.x/user-guide/html/ch04s03.html
for more info for more info
--> -->
<groupId>com.springsource.bundlor</groupId> <groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId> <artifactId>com.springsource.bundlor.maven</artifactId>
<version>1.0.0.RELEASE</version> <version>1.0.0.RELEASE</version>
<configuration> <configuration>
<failOnWarnings>true</failOnWarnings> <failOnWarnings>true</failOnWarnings>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
<id>bundlor</id> <id>bundlor</id>
<goals> <goals>
<goal>bundlor</goal> <goal>bundlor</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</pluginManagement> </pluginManagement>
</build> </build>
<pluginRepositories> <pluginRepositories>
<pluginRepository> <pluginRepository>
<!-- necessary for bundlor and utils --> <!-- necessary for bundlor and utils -->
<id>repository.plugin.springsource.release</id> <id>repository.plugin.springsource.release</id>
<name>SpringSource Maven Repository</name> <name>SpringSource Maven Repository</name>
<url>http://repository.springsource.com/maven/bundles/release</url> <url>http://repository.springsource.com/maven/bundles/release</url>
</pluginRepository> </pluginRepository>
</pluginRepositories> </pluginRepositories>
<repositories> <repositories>
<repository> <repository>
<id>repository.springframework.maven.release</id> <id>repository.springframework.maven.release</id>
<name>Spring Framework Maven Release Repository</name> <name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url> <url>http://maven.springframework.org/release</url>
</repository> </repository>
<repository> <repository>
<id>repository.springframework.maven.milestone</id> <id>repository.springframework.maven.milestone</id>
<name>Spring Framework Maven Milestone Repository</name> <name>Spring Framework Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url> <url>http://maven.springframework.org/milestone</url>
</repository> </repository>
<repository> <repository>
<id>repository.springframework.maven.snapshot</id> <id>repository.springframework.maven.snapshot</id>
<name>Spring Framework Maven Snapshot Repository</name> <name>Spring Framework Maven Snapshot Repository</name>
<url>http://maven.springframework.org/snapshot</url> <url>http://maven.springframework.org/snapshot</url>
</repository> </repository>
</repositories> </repositories>
<reporting> <reporting>
<plugins> <plugins>
<plugin> <plugin>
<!-- <!--
significantly speeds up the 'Dependencies' report during site significantly speeds up the 'Dependencies' report during site
creation see creation see
http://old.nabble.com/Skipping-dependency-report-during-Maven2-site-generation-td20116761.html http://old.nabble.com/Skipping-dependency-report-during-Maven2-site-generation-td20116761.html
--> -->
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId> <artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.1</version> <version>2.1</version>
<configuration> <configuration>
<dependencyLocationsEnabled>false</dependencyLocationsEnabled> <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</reporting> </reporting>
</project> </project>

View File

@@ -1,120 +1,132 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<modelVersion>4.0.0</modelVersion> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId> <parent>
<artifactId>spring-data-document-parent</artifactId> <groupId>org.springframework.data</groupId>
<version>1.0.0.BUILD-SNAPSHOT</version> <artifactId>spring-data-document-parent</artifactId>
<relativePath>../spring-data-document-parent/pom.xml</relativePath> <version>1.0.0.BUILD-SNAPSHOT</version>
</parent> <relativePath>../spring-data-document-parent/pom.xml</relativePath>
<artifactId>spring-data-mongodb</artifactId> </parent>
<packaging>jar</packaging> <artifactId>spring-data-mongodb</artifactId>
<name>Spring Data MongoDB Support</name> <packaging>jar</packaging>
<dependencies> <name>Spring Data MongoDB Support</name>
<dependencies>
<!-- Spring --> <!-- Spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId> <artifactId>spring-beans</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId> <artifactId>spring-tx</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId> <artifactId>spring-expression</artifactId>
<scope>test</scope> </dependency>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Data --> <!-- Spring Data -->
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-document-core</artifactId> <artifactId>spring-data-document-core</artifactId>
</dependency> <version>1.0.0.BUILD-SNAPSHOT</version>
<dependency> </dependency>
<groupId>org.springframework.data</groupId> <dependency>
<artifactId>spring-data-commons-core</artifactId> <groupId>org.springframework.data</groupId>
</dependency> <artifactId>spring-data-commons-core</artifactId>
</dependency>
<!-- Logging --> <!-- Logging -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId> <artifactId>jcl-over-slf4j</artifactId>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId> <artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>javax.mail</groupId> <groupId>javax.mail</groupId>
<artifactId>mail</artifactId> <artifactId>mail</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>javax.jms</groupId> <groupId>javax.jms</groupId>
<artifactId>jms</artifactId> <artifactId>jms</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>com.sun.jdmk</groupId> <groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId> <artifactId>jmxtools</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>com.sun.jmx</groupId> <groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId> <artifactId>jmxri</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>javax.annotation</groupId>
<artifactId>mockito-all</artifactId> <artifactId>jsr250-api</artifactId>
<scope>test</scope> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>javax.persistence</groupId>
<artifactId>junit</artifactId> <artifactId>persistence-api</artifactId>
</dependency> <version>1.0</version>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.3</version>
</dependency>
</dependencies> <dependency>
<build> <groupId>org.mockito</groupId>
<plugins> <artifactId>mockito-all</artifactId>
<plugin> <scope>test</scope>
<groupId>com.springsource.bundlor</groupId> </dependency>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin> <dependency>
</plugins> <groupId>org.hamcrest</groupId>
</build> <artifactId>hamcrest-all</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
</project> </project>

View File

@@ -15,225 +15,222 @@
*/ */
package org.springframework.data.document.mongodb; package org.springframework.data.document.mongodb;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.*;
/** /**
* An iterable of {@link MongoPropertyDescriptor}s that allows dedicated access to the {@link MongoPropertyDescriptor} * An iterable of {@link MongoPropertyDescriptor}s that allows dedicated access to the {@link MongoPropertyDescriptor}
* that captures the id-property. * that captures the id-property.
* *
* @author Oliver Gierke * @author Oliver Gierke
*/ */
public class MongoPropertyDescriptors implements Iterable<MongoPropertyDescriptors.MongoPropertyDescriptor> { public class MongoPropertyDescriptors implements Iterable<MongoPropertyDescriptors.MongoPropertyDescriptor> {
private final Collection<MongoPropertyDescriptors.MongoPropertyDescriptor> descriptors; private final Collection<MongoPropertyDescriptors.MongoPropertyDescriptor> descriptors;
private final MongoPropertyDescriptors.MongoPropertyDescriptor idDescriptor; private final MongoPropertyDescriptors.MongoPropertyDescriptor idDescriptor;
/** /**
* Creates the {@link MongoPropertyDescriptors} for the given type. * Creates the {@link MongoPropertyDescriptors} for the given type.
* *
* @param type * @param type
*/ */
public MongoPropertyDescriptors(Class<?> type) { public MongoPropertyDescriptors(Class<?> type) {
Assert.notNull(type); Assert.notNull(type);
Set<MongoPropertyDescriptors.MongoPropertyDescriptor> descriptors = new HashSet<MongoPropertyDescriptors.MongoPropertyDescriptor>(); Set<MongoPropertyDescriptors.MongoPropertyDescriptor> descriptors = new HashSet<MongoPropertyDescriptors.MongoPropertyDescriptor>();
MongoPropertyDescriptors.MongoPropertyDescriptor idDesciptor = null; MongoPropertyDescriptors.MongoPropertyDescriptor idDesciptor = null;
for (PropertyDescriptor candidates : BeanUtils.getPropertyDescriptors(type)) { for (PropertyDescriptor candidates : BeanUtils.getPropertyDescriptors(type)) {
MongoPropertyDescriptor descriptor = new MongoPropertyDescriptors.MongoPropertyDescriptor(candidates); MongoPropertyDescriptor descriptor = new MongoPropertyDescriptors.MongoPropertyDescriptor(candidates);
descriptors.add(descriptor); descriptors.add(descriptor);
if (descriptor.isIdProperty()) { if (descriptor.isIdProperty()) {
idDesciptor = descriptor; idDesciptor = descriptor;
} }
} }
this.descriptors = Collections.unmodifiableSet(descriptors); this.descriptors = Collections.unmodifiableSet(descriptors);
this.idDescriptor = idDesciptor; this.idDescriptor = idDesciptor;
} }
/** /**
* Returns the {@link MongoPropertyDescriptor} for the id property. * Returns the {@link MongoPropertyDescriptor} for the id property.
* *
* @return the idDescriptor * @return the idDescriptor
*/ */
public MongoPropertyDescriptors.MongoPropertyDescriptor getIdDescriptor() { public MongoPropertyDescriptors.MongoPropertyDescriptor getIdDescriptor() {
return idDescriptor; return idDescriptor;
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see java.lang.Iterable#iterator() * @see java.lang.Iterable#iterator()
*/ */
public Iterator<MongoPropertyDescriptors.MongoPropertyDescriptor> iterator() { public Iterator<MongoPropertyDescriptors.MongoPropertyDescriptor> iterator() {
return descriptors.iterator(); return descriptors.iterator();
} }
/** /**
* Simple value object to have a more suitable abstraction for MongoDB specific property handling. * Simple value object to have a more suitable abstraction for MongoDB specific property handling.
* *
* @author Oliver Gierke * @author Oliver Gierke
*/ */
public static class MongoPropertyDescriptor { public static class MongoPropertyDescriptor {
public static Collection<Class<?>> SUPPORTED_ID_CLASSES;
static {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(ObjectId.class);
classes.add(String.class);
classes.add(BigInteger.class);
SUPPORTED_ID_CLASSES = Collections.unmodifiableCollection(classes);
}
private static final String ID_PROPERTY = "id"; public static Collection<Class<?>> SUPPORTED_ID_CLASSES;
static final String ID_KEY = "_id";
private final PropertyDescriptor delegate; static {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(ObjectId.class);
classes.add(String.class);
classes.add(BigInteger.class);
SUPPORTED_ID_CLASSES = Collections.unmodifiableCollection(classes);
}
/** private static final String ID_PROPERTY = "id";
* Creates a new {@link MongoPropertyDescriptor} for the given {@link PropertyDescriptor}. static final String ID_KEY = "_id";
*
* @param descriptor
*/
public MongoPropertyDescriptor(PropertyDescriptor descriptor) {
Assert.notNull(descriptor);
this.delegate = descriptor;
}
/** private final PropertyDescriptor delegate;
* Returns whether the property is the id-property. Will be identified by name for now ({@value #ID_PROPERTY}).
*
* @return
*/
boolean isIdProperty() {
return ID_PROPERTY.equals(delegate.getName()) || ID_KEY.equals(delegate.getName());
}
/** /**
* Returns whether the property is of one of the supported id types. Currently we support {@link String}, * Creates a new {@link MongoPropertyDescriptor} for the given {@link PropertyDescriptor}.
* {@link ObjectId} and {@link BigInteger}. *
* * @param descriptor
* @return */
*/ public MongoPropertyDescriptor(PropertyDescriptor descriptor) {
public boolean isOfIdType() { Assert.notNull(descriptor);
return SUPPORTED_ID_CLASSES.contains(delegate.getPropertyType()); this.delegate = descriptor;
} }
/** /**
* Returns the key that shall be used for mapping. Will return {@value #ID_KEY} for the id property and the * Returns whether the property is the id-property. Will be identified by name for now ({@value #ID_PROPERTY}).
* plain name for all other ones. *
* * @return
* @return */
*/ public boolean isIdProperty() {
public String getKeyToMap() { return ID_PROPERTY.equals(delegate.getName()) || ID_KEY.equals(delegate.getName());
return isIdProperty() ? ID_KEY : delegate.getName(); }
}
/** /**
* Returns the name of the property. * Returns whether the property is of one of the supported id types. Currently we support {@link String},
* * {@link ObjectId} and {@link BigInteger}.
* @return *
*/ * @return
public String getName() { */
return delegate.getName(); public boolean isOfIdType() {
} return SUPPORTED_ID_CLASSES.contains(delegate.getPropertyType());
}
/** /**
* Returns whether the underlying property is actually mappable. By default this will exclude the * Returns the key that shall be used for mapping. Will return {@value #ID_KEY} for the id property and the
* {@literal class} property and only include properties with a getter. * plain name for all other ones.
* *
* @return * @return
*/ */
public boolean isMappable() { public String getKeyToMap() {
return !delegate.getName().equals("class") && delegate.getReadMethod() != null; return isIdProperty() ? ID_KEY : delegate.getName();
} }
/** /**
* Returns the plain property type. * Returns the name of the property.
* *
* @return * @return
*/ */
public Class<?> getPropertyType() { public String getName() {
return delegate.getPropertyType(); return delegate.getName();
} }
/** /**
* Returns the type type to be set. Will return the setter method's type and fall back to the getter method's * Returns whether the underlying property is actually mappable. By default this will exclude the
* return type in case no setter is available. Useful for further (generics) inspection. * {@literal class} property and only include properties with a getter.
* *
* @return * @return
*/ */
public Type getTypeToSet() { public boolean isMappable() {
return !delegate.getName().equals("class") && delegate.getReadMethod() != null;
}
Method method = delegate.getWriteMethod(); /**
return method == null ? delegate.getReadMethod().getGenericReturnType() * Returns the plain property type.
: method.getGenericParameterTypes()[0]; *
} * @return
*/
public Class<?> getPropertyType() {
return delegate.getPropertyType();
}
/** /**
* Returns whther we describe a {@link Map}. * Returns the type type to be set. Will return the setter method's type and fall back to the getter method's
* @return * return type in case no setter is available. Useful for further (generics) inspection.
*/ *
public boolean isMap() { * @return
return Map.class.isAssignableFrom(getPropertyType()); */
} public Type getTypeToSet() {
/** Method method = delegate.getWriteMethod();
* Returns whether the descriptor is for a collection. return method == null ? delegate.getReadMethod().getGenericReturnType()
* @return : method.getGenericParameterTypes()[0];
*/ }
public boolean isCollection() {
return Collection.class.isAssignableFrom(getPropertyType());
}
/** /**
* Returns whether the descriptor is for an {@link Enum}. * Returns whther we describe a {@link Map}.
* *
* @return * @return
*/ */
public boolean isEnum() { public boolean isMap() {
return Enum.class.isAssignableFrom(getPropertyType()); return Map.class.isAssignableFrom(getPropertyType());
} }
/* /**
* (non-Javadoc) * Returns whether the descriptor is for a collection.
* *
* @see java.lang.Object#equals(java.lang.Object) * @return
*/ */
@Override public boolean isCollection() {
public boolean equals(Object obj) { return Collection.class.isAssignableFrom(getPropertyType());
if (obj == this) { }
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
MongoPropertyDescriptor that = (MongoPropertyDescriptor) obj;
return that.delegate.equals(this.delegate);
}
/* /**
* (non-Javadoc) * Returns whether the descriptor is for an {@link Enum}.
* *
* @see java.lang.Object#hashCode() * @return
*/ */
@Override public boolean isEnum() {
public int hashCode() { return Enum.class.isAssignableFrom(getPropertyType());
return delegate.hashCode(); }
}
} /*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !getClass().equals(obj.getClass())) {
return false;
}
MongoPropertyDescriptor that = (MongoPropertyDescriptor) obj;
return that.delegate.equals(this.delegate);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return delegate.hashCode();
}
}
} }

View File

@@ -19,22 +19,22 @@ import com.mongodb.DBObject;
/** /**
* A MongoWriter is responsible for converting a native MongoDB DBObject to an object of type T. * A MongoWriter is responsible for converting a native MongoDB DBObject to an object of type T.
* *
* @param <T> the type of the object to convert from a DBObject
* @author Mark Pollack * @author Mark Pollack
* @author Thomas Risberg * @author Thomas Risberg
* @author Oliver Gierke * @author Oliver Gierke
*
* @param <T> the type of the object to convert from a DBObject
*/ */
public interface MongoReader<T> { public interface MongoReader<T> {
/** /**
* Ready from the native MongoDB DBObject representation to an instance of the class T. The given type has to be the * Ready from the native MongoDB DBObject representation to an instance of the class T. The given type has to be the
* starting point for marshalling the {@link DBObject} into it. So in case there's no real valid data inside * starting point for marshalling the {@link DBObject} into it. So in case there's no real valid data inside
* {@link DBObject} for the given type, just return an empty instance of the given type. * {@link DBObject} for the given type, just return an empty instance of the given type.
* @param clazz the type of the return value *
* @param dbo theDBObject * @param clazz the type of the return value
* @return the converted object * @param dbo theDBObject
*/ * @return the converted object
<S extends T> S read(Class<S> clazz, DBObject dbo); */
<S extends T> S read(Class<S> clazz, DBObject dbo);
} }

View File

@@ -36,6 +36,8 @@ import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoP
import org.springframework.data.document.mongodb.query.IndexDefinition; import org.springframework.data.document.mongodb.query.IndexDefinition;
import org.springframework.data.document.mongodb.query.Query; import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.document.mongodb.query.Update; import org.springframework.data.document.mongodb.query.Update;
import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.convert.SimpleMongoConverter;
import org.springframework.jca.cci.core.ConnectionCallback; import org.springframework.jca.cci.core.ConnectionCallback;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@@ -130,7 +132,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
} }
/** /**
* Constructor used for a template configuration with a default collection name and a custom {@link MongoConverter} * Constructor used for a template configuration with a default collection name and a custom {@link org.springframework.data.document.mongodb.convert.MongoConverter}
* @param mongo * @param mongo
* @param databaseName * @param databaseName
* @param defaultCollectionName * @param defaultCollectionName
@@ -205,7 +207,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations {
} }
/** /**
* Returns the default {@link MongoConverter}. * Returns the default {@link org.springframework.data.document.mongodb.convert.MongoConverter}.
* *
* @return * @return
*/ */

View File

@@ -1,557 +0,0 @@
/*
* Copyright 2010-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.comparator.CompoundComparator;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
/**
* Basic {@link MongoConverter} implementation to convert between domain classes and {@link DBObject}s.
*
* @author Mark Pollack
* @author Thomas Risberg
* @author Oliver Gierke
*/
public class SimpleMongoConverter implements MongoConverter {
private static final Log LOG = LogFactory.getLog(SimpleMongoConverter.class);
private static final Set<String> SIMPLE_TYPES;
static {
Set<String> basics = new HashSet<String>();
basics.add(boolean.class.getName());
basics.add(long.class.getName());
basics.add(short.class.getName());
basics.add(int.class.getName());
basics.add(byte.class.getName());
basics.add(float.class.getName());
basics.add(double.class.getName());
basics.add(char.class.getName());
basics.add(Boolean.class.getName());
basics.add(Long.class.getName());
basics.add(Short.class.getName());
basics.add(Integer.class.getName());
basics.add(Byte.class.getName());
basics.add(Float.class.getName());
basics.add(Double.class.getName());
basics.add(Character.class.getName());
basics.add(String.class.getName());
basics.add(java.util.Date.class.getName());
// basics.add(Time.class.getName());
// basics.add(Timestamp.class.getName());
// basics.add(java.sql.Date.class.getName());
// basics.add(BigDecimal.class.getName());
// basics.add(BigInteger.class.getName());
basics.add(Locale.class.getName());
// basics.add(Calendar.class.getName());
// basics.add(GregorianCalendar.class.getName());
// basics.add(java.util.Currency.class.getName());
// basics.add(TimeZone.class.getName());
// basics.add(Object.class.getName());
basics.add(Class.class.getName());
// basics.add(byte[].class.getName());
// basics.add(Byte[].class.getName());
// basics.add(char[].class.getName());
// basics.add(Character[].class.getName());
// basics.add(Blob.class.getName());
// basics.add(Clob.class.getName());
// basics.add(Serializable.class.getName());
// basics.add(URI.class.getName());
// basics.add(URL.class.getName());
basics.add(DBRef.class.getName());
basics.add(Pattern.class.getName());
basics.add(CodeWScope.class.getName());
basics.add(ObjectId.class.getName());
// TODO check on enums..
basics.add(Enum.class.getName());
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
}
private final GenericConversionService conversionService;
/**
* Creates a {@link SimpleMongoConverter}.
*/
public SimpleMongoConverter() {
this.conversionService = ConversionServiceFactory.createDefaultConversionService();
initializeConverters();
}
/**
* Creates a new {@link SimpleMongoConverter} for the given {@link ConversionService}.
*
* @param conversionService
*/
public SimpleMongoConverter(GenericConversionService conversionService) {
Assert.notNull(conversionService);
this.conversionService = conversionService;
initializeConverters();
}
/**
* Initializes additional converters that handle {@link ObjectId} conversion. Will register converters for supported
* id types if none are registered for those conversion already. {@link GenericConversionService} is configured.
*/
protected void initializeConverters() {
if (!conversionService.canConvert(ObjectId.class, String.class)) {
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
}
if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) {
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
conversionService.addConverter(BigIntegerToIdConverter.INSTANCE);
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.document.mongodb.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
*/
@SuppressWarnings("rawtypes")
public void write(Object obj, DBObject dbo) {
MongoBeanWrapper beanWrapper = createWrapper(obj, false);
for (MongoPropertyDescriptor descriptor : beanWrapper.getDescriptors()) {
if (descriptor.isMappable()) {
Object value = beanWrapper.getValue(descriptor);
if (value == null) {
continue;
}
String keyToUse = descriptor.getKeyToMap();
// TODO validate Enums...
if (descriptor.isEnum()) {
writeValue(dbo, keyToUse, ((Enum) value).name());
} else if (descriptor.isIdProperty() && descriptor.isOfIdType()) {
if (value instanceof String && ObjectId.isValid((String)value)) {
try {
writeValue(dbo, keyToUse, conversionService.convert(value, ObjectId.class));
} catch (ConversionFailedException iae) {
LOG.warn("Unable to convert the String " + value + " to an ObjectId");
writeValue(dbo, keyToUse, value);
}
}
else {
// we can't convert this id - use as is
writeValue(dbo, keyToUse, value);
}
} else {
writeValue(dbo, keyToUse, value);
}
} else {
if (!"class".equals(descriptor.getName())) {
LOG.warn("Unable to map property " + descriptor.getName() + ". Skipping.");
}
}
}
}
/**
* Writes the given value to the given {@link DBObject}. Will skip {@literal null} values.
*
* @param dbo
* @param keyToUse
* @param value
*/
private void writeValue(DBObject dbo, String keyToUse, Object value) {
if (!isSimpleType(value.getClass())) {
writeCompoundValue(dbo, keyToUse, value);
} else {
dbo.put(keyToUse, value);
}
}
/**
* Writes the given {@link CompoundComparator} value to the given {@link DBObject}.
*
* @param dbo
* @param keyToUse
* @param value
*/
@SuppressWarnings("unchecked")
private void writeCompoundValue(DBObject dbo, String keyToUse, Object value) {
if (value instanceof Map) {
writeMap(dbo, keyToUse, (Map<String, Object>) value);
return;
}
if (value instanceof Collection) {
// Should write a collection!
writeArray(dbo, keyToUse, ((Collection<Object>) value).toArray());
return;
}
if (value instanceof Object[]) {
// Should write an array!
writeArray(dbo, keyToUse, (Object[]) value);
return;
}
DBObject nestedDbo = new BasicDBObject();
write(value, nestedDbo);
dbo.put(keyToUse, nestedDbo);
}
/**
* Writes the given {@link Map} to the given {@link DBObject}.
*
* @param dbo
* @param mapKey
* @param map
*/
protected void writeMap(DBObject dbo, String mapKey, Map<String, Object> map) {
// TODO support non-string based keys as long as there is a Spring Converter obj->string and (optionally)
// string->obj
DBObject dboToPopulate = null;
// TODO - Does that make sense? If we create a new object here it's content will never make it out of this
// method
if (mapKey != null) {
dboToPopulate = new BasicDBObject();
} else {
dboToPopulate = dbo;
}
if (map != null) {
for (Entry<String, Object> entry : map.entrySet()) {
Object entryValue = entry.getValue();
String entryKey = entry.getKey();
if (!isSimpleType(entryValue.getClass())) {
writeCompoundValue(dboToPopulate, entryKey, entryValue);
} else {
dboToPopulate.put(entryKey, entryValue);
}
}
dbo.put(mapKey, dboToPopulate);
}
}
/**
* Writes the given array to the given {@link DBObject}.
*
* @param dbo
* @param keyToUse
* @param array
*/
protected void writeArray(DBObject dbo, String keyToUse, Object[] array) {
// TODO
Object[] dboValues;
if (array != null) {
dboValues = new Object[array.length];
int i = 0;
for (Object o : array) {
if (!isSimpleType(o.getClass())) {
DBObject dboValue = new BasicDBObject();
write(o, dboValue);
dboValues[i] = dboValue;
} else {
dboValues[i] = o;
}
i++;
}
dbo.put(keyToUse, dboValues);
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.document.mongodb.MongoReader#read(java.lang.Class, com.mongodb.DBObject)
*/
public <S> S read(Class<S> clazz, DBObject source) {
if (source == null) {
return null;
}
Assert.notNull(clazz, "Mapped class was not specified");
S target = BeanUtils.instantiateClass(clazz);
MongoBeanWrapper bw = new MongoBeanWrapper(target, conversionService, true);
for (MongoPropertyDescriptor descriptor : bw.getDescriptors()) {
String keyToUse = descriptor.getKeyToMap();
if (source.containsField(keyToUse)) {
if (descriptor.isMappable()) {
Object value = source.get(keyToUse);
if (!isSimpleType(value.getClass())) {
if (value instanceof Object[]) {
bw.setValue(descriptor, readCollection(descriptor, Arrays.asList((Object[]) value))
.toArray());
} else if (value instanceof BasicDBList) {
bw.setValue(descriptor, readCollection(descriptor, (BasicDBList) value));
} else if (value instanceof DBObject) {
bw.setValue(descriptor, readCompoundValue(descriptor, (DBObject) value));
} else {
LOG.warn("Unable to map compound DBObject field " + keyToUse + " to property "
+ descriptor.getName()
+ ". The field value should have been a 'DBObject.class' but was "
+ value.getClass().getName());
}
} else {
bw.setValue(descriptor, value);
}
} else {
LOG.warn("Unable to map DBObject field " + keyToUse + " to property " + descriptor.getName()
+ ". Skipping.");
}
}
}
return target;
}
/**
* Reads the given collection values (that are {@link DBObject}s potentially) into a {@link Collection} of domain
* objects.
*
* @param descriptor
* @param values
* @return
*/
private Collection<Object> readCollection(MongoPropertyDescriptor descriptor, Collection<?> values) {
Class<?> targetCollectionType = descriptor.getPropertyType();
boolean targetIsArray = targetCollectionType.isArray();
@SuppressWarnings("unchecked")
Collection<Object> result = targetIsArray ? new ArrayList<Object>(values.size()) : CollectionFactory
.createCollection(targetCollectionType, values.size());
for (Object o : values) {
if (o instanceof DBObject) {
Class<?> type;
if (targetIsArray) {
type = targetCollectionType.getComponentType();
} else {
type = getGenericParameters(descriptor.getTypeToSet()).get(0);
}
result.add(read(type, (DBObject) o));
} else {
result.add(o);
}
}
return result;
}
/**
* Reads a compound value from the given {@link DBObject} for the given property.
*
* @param pd
* @param dbo
* @return
*/
private Object readCompoundValue(MongoPropertyDescriptors.MongoPropertyDescriptor pd, DBObject dbo) {
Assert.isTrue(!pd.isCollection(), "Collections not supported!");
if (pd.isMap()) {
return readMap(pd, dbo, getGenericParameters(pd.getTypeToSet()).get(1));
} else {
return read(pd.getPropertyType(), dbo);
}
}
/**
* Create a {@link Map} instance. Will return a {@link HashMap} by default. Subclasses might want to override this
* method to use a custom {@link Map} implementation.
*
* @return
*/
protected Map<String, Object> createMap() {
return new HashMap<String, Object>();
}
/**
* Reads every key/value pair from the {@link DBObject} into a {@link Map} instance.
*
* @param pd
* @param dbo
* @param targetType
* @return
*/
protected Map<?, ?> readMap(MongoPropertyDescriptors.MongoPropertyDescriptor pd, DBObject dbo, Class<?> targetType) {
Map<String, Object> map = createMap();
for (String key : dbo.keySet()) {
Object value = dbo.get(key);
if (!isSimpleType(value.getClass())) {
map.put(key, read(targetType, (DBObject) value));
// Can do some reflection tricks here -
// throw new RuntimeException("User types not supported yet as values for Maps");
} else {
map.put(key, conversionService.convert(value, targetType));
}
}
return map;
}
protected static boolean isSimpleType(Class<?> propertyType) {
if (propertyType == null) {
return false;
}
if (propertyType.isArray()) {
return isSimpleType(propertyType.getComponentType());
}
return SIMPLE_TYPES.contains(propertyType.getName());
}
/**
* Callback to allow customizing creation of a {@link MongoBeanWrapper}.
*
* @param target the target object to wrap
* @param fieldAccess whether to use field access or property access
* @return
*/
protected MongoBeanWrapper createWrapper(Object target, boolean fieldAccess) {
return new MongoBeanWrapper(target, conversionService, fieldAccess);
}
List<Class<?>> getGenericParameters(Type genericParameterType) {
List<Class<?>> actualGenericParameterTypes = new ArrayList<Class<?>>();
if (genericParameterType instanceof ParameterizedType) {
ParameterizedType aType = (ParameterizedType) genericParameterType;
Type[] parameterArgTypes = aType.getActualTypeArguments();
for (Type parameterArgType : parameterArgTypes) {
if (parameterArgType instanceof GenericArrayType) {
Class<?> arrayType = (Class<?>) ((GenericArrayType) parameterArgType).getGenericComponentType();
actualGenericParameterTypes.add(Array.newInstance(arrayType, 0).getClass());
} else {
if (parameterArgType instanceof ParameterizedType) {
ParameterizedType paramTypeArgs = (ParameterizedType) parameterArgType;
actualGenericParameterTypes.add((Class<?>) paramTypeArgs.getRawType());
} else {
if (parameterArgType instanceof TypeVariable) {
throw new RuntimeException("Can not map " + ((TypeVariable<?>) parameterArgType).getName());
} else {
if (parameterArgType instanceof Class) {
actualGenericParameterTypes.add((Class<?>) parameterArgType);
} else {
throw new RuntimeException("Can not map " + parameterArgType);
}
}
}
}
}
}
return actualGenericParameterTypes;
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoConverter#convertObjectId(org.bson.types.ObjectId, java.lang.Class)
*/
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
return conversionService.convert(id, targetType);
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.MongoConverter#convertObjectId(java.lang.Object)
*/
public ObjectId convertObjectId(Object id) {
return conversionService.convert(id, ObjectId.class);
}
/**
* Simple singleton to convert {@link ObjectId}s to their {@link String} representation.
*
* @author Oliver Gierke
*/
public static enum ObjectIdToStringConverter implements Converter<ObjectId, String> {
INSTANCE;
public String convert(ObjectId id) {
return id.toString();
}
}
/**
* Simple singleton to convert {@link String}s to their {@link ObjectId} representation.
*
* @author Oliver Gierke
*/
public static enum StringToObjectIdConverter implements Converter<String, ObjectId> {
INSTANCE;
public ObjectId convert(String source) {
return new ObjectId(source);
}
}
/**
* Simple singleton to convert {@link ObjectId}s to their {@link BigInteger} representation.
*
* @author Oliver Gierke
*/
public static enum ObjectIdToBigIntegerConverter implements Converter<ObjectId, BigInteger> {
INSTANCE;
public BigInteger convert(ObjectId source) {
return new BigInteger(source.toString(), 16);
}
}
/**
* Simple singleton to convert {@link BigInteger}s to their {@link ObjectId} representation.
*
* @author Oliver Gierke
*/
public static enum BigIntegerToIdConverter implements Converter<BigInteger, ObjectId> {
INSTANCE;
public ObjectId convert(BigInteger source) {
return new ObjectId(source.toString(16));
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.config;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Created by IntelliJ IDEA.
* User: jbrisbin
* Date: 2/28/11
* Time: 9:26 AM
* To change this template use File | Settings | File Templates.
*/
public class MongoMappingConverterParser extends AbstractSingleBeanDefinitionParser {
private static final String BASE_PACKAGE = "base-package";
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
return "mongoMappingConverter";
}
return id;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
}
}

View File

@@ -29,167 +29,164 @@ import org.w3c.dom.Element;
* {@link RepositoryConfig} implementation to create * {@link RepositoryConfig} implementation to create
* {@link MongoRepositoryConfiguration} instances for both automatic and manual * {@link MongoRepositoryConfiguration} instances for both automatic and manual
* configuration. * configuration.
* *
* @author Oliver Gierke * @author Oliver Gierke
*/ */
public class SimpleMongoRepositoryConfiguration public class SimpleMongoRepositoryConfiguration extends RepositoryConfig<SimpleMongoRepositoryConfiguration.MongoRepositoryConfiguration, SimpleMongoRepositoryConfiguration> {
extends
RepositoryConfig<SimpleMongoRepositoryConfiguration.MongoRepositoryConfiguration, SimpleMongoRepositoryConfiguration> {
private static final String MONGO_TEMPLATE_REF = "mongo-template-ref"; private static final String MONGO_TEMPLATE_REF = "mongo-template-ref";
private static final String DEFAULT_MONGO_TEMPLATE_REF = "mongoTemplate"; private static final String DEFAULT_MONGO_TEMPLATE_REF = "mongoTemplate";
/**
* Creates a new {@link SimpleMongoRepositoryConfiguration} for the given
* {@link Element}.
*
* @param repositoriesElement
*/
protected SimpleMongoRepositoryConfiguration(Element repositoriesElement) {
super(repositoriesElement, MongoRepositoryFactoryBean.class.getName());
}
/**
* Returns the bean name of the {@link org.springframework.data.document.mongodb.MongoTemplate} to be referenced.
*
* @return
*/
public String getMongoTemplateRef() {
String templateRef = getSource().getAttribute(MONGO_TEMPLATE_REF);
return StringUtils.hasText(templateRef) ? templateRef
: DEFAULT_MONGO_TEMPLATE_REF;
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.config.GlobalRepositoryConfigInformation
* #getAutoconfigRepositoryInformation(java.lang.String)
*/
public MongoRepositoryConfiguration getAutoconfigRepositoryInformation(
String interfaceName) {
return new AutomaticMongoRepositoryConfiguration(interfaceName, this);
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.config.GlobalRepositoryConfigInformation
* #getRepositoryBaseInterface()
*/
public Class<?> getRepositoryBaseInterface() {
return MongoRepository.class;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.config.RepositoryConfig#
* createSingleRepositoryConfigInformationFor(org.w3c.dom.Element)
*/
@Override
protected MongoRepositoryConfiguration createSingleRepositoryConfigInformationFor(
Element element) {
return new ManualMongoRepositoryConfiguration(element, this);
}
/**
* Simple interface for configuration values specific to Mongo repositories.
*
* @author Oliver Gierke
*/
public interface MongoRepositoryConfiguration
extends
SingleRepositoryConfigInformation<SimpleMongoRepositoryConfiguration> {
String getMongoTemplateRef();
}
/**
* Implements manual lookup of the additional attributes.
*
* @author Oliver Gierke
*/
private static class ManualMongoRepositoryConfiguration
extends
ManualRepositoryConfigInformation<SimpleMongoRepositoryConfiguration>
implements MongoRepositoryConfiguration {
/** /**
* Creates a new {@link SimpleMongoRepositoryConfiguration} for the given * Creates a new {@link ManualMongoRepositoryConfiguration} for the
* {@link Element}. * given {@link Element} and parent.
* *
* @param repositoriesElement * @param element
* @param defaultRepositoryFactoryBeanClassName * @param parent
*/ */
protected SimpleMongoRepositoryConfiguration(Element repositoriesElement) { public ManualMongoRepositoryConfiguration(Element element,
SimpleMongoRepositoryConfiguration parent) {
super(repositoriesElement, MongoRepositoryFactoryBean.class.getName()); super(element, parent);
} }
/** /*
* Returns the bean name of the {@link org.springframework.data.document.mongodb.MongoTemplate} to be referenced. * (non-Javadoc)
* *
* @return * @see org.springframework.data.document.mongodb.repository.config.
*/ * SimpleMongoRepositoryConfiguration
* .MongoRepositoryConfiguration#getMongoTemplateRef()
*/
public String getMongoTemplateRef() { public String getMongoTemplateRef() {
String templateRef = getSource().getAttribute(MONGO_TEMPLATE_REF); return getAttribute(MONGO_TEMPLATE_REF);
return StringUtils.hasText(templateRef) ? templateRef }
: DEFAULT_MONGO_TEMPLATE_REF; }
/**
* Implements the lookup of the additional attributes during automatic
* configuration.
*
* @author Oliver Gierke
*/
private static class AutomaticMongoRepositoryConfiguration
extends
AutomaticRepositoryConfigInformation<SimpleMongoRepositoryConfiguration>
implements MongoRepositoryConfiguration {
/**
* Creates a new {@link AutomaticMongoRepositoryConfiguration} for the
* given interface and parent.
*
* @param interfaceName
* @param parent
*/
public AutomaticMongoRepositoryConfiguration(String interfaceName,
SimpleMongoRepositoryConfiguration parent) {
super(interfaceName, parent);
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see * @see org.springframework.data.document.mongodb.repository.config.
* org.springframework.data.repository.config.GlobalRepositoryConfigInformation * SimpleMongoRepositoryConfiguration
* #getAutoconfigRepositoryInformation(java.lang.String) * .MongoRepositoryConfiguration#getMongoTemplateRef()
*/ */
public MongoRepositoryConfiguration getAutoconfigRepositoryInformation( public String getMongoTemplateRef() {
String interfaceName) {
return new AutomaticMongoRepositoryConfiguration(interfaceName, this); return getParent().getMongoTemplateRef();
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.config.GlobalRepositoryConfigInformation
* #getRepositoryBaseInterface()
*/
public Class<?> getRepositoryBaseInterface() {
return MongoRepository.class;
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.repository.config.RepositoryConfig#
* createSingleRepositoryConfigInformationFor(org.w3c.dom.Element)
*/
@Override
protected MongoRepositoryConfiguration createSingleRepositoryConfigInformationFor(
Element element) {
return new ManualMongoRepositoryConfiguration(element, this);
}
/**
* Simple interface for configuration values specific to Mongo repositories.
*
* @author Oliver Gierke
*/
public interface MongoRepositoryConfiguration
extends
SingleRepositoryConfigInformation<SimpleMongoRepositoryConfiguration> {
String getMongoTemplateRef();
}
/**
* Implements manual lookup of the additional attributes.
*
* @author Oliver Gierke
*/
private static class ManualMongoRepositoryConfiguration
extends
ManualRepositoryConfigInformation<SimpleMongoRepositoryConfiguration>
implements MongoRepositoryConfiguration {
/**
* Creates a new {@link ManualMongoRepositoryConfiguration} for the
* given {@link Element} and parent.
*
* @param element
* @param parent
*/
public ManualMongoRepositoryConfiguration(Element element,
SimpleMongoRepositoryConfiguration parent) {
super(element, parent);
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.document.mongodb.repository.config.
* SimpleMongoRepositoryConfiguration
* .MongoRepositoryConfiguration#getMongoTemplateRef()
*/
public String getMongoTemplateRef() {
return getAttribute(MONGO_TEMPLATE_REF);
}
}
/**
* Implements the lookup of the additional attributes during automatic
* configuration.
*
* @author Oliver Gierke
*/
private static class AutomaticMongoRepositoryConfiguration
extends
AutomaticRepositoryConfigInformation<SimpleMongoRepositoryConfiguration>
implements MongoRepositoryConfiguration {
/**
* Creates a new {@link AutomaticMongoRepositoryConfiguration} for the
* given interface and parent.
*
* @param interfaceName
* @param parent
*/
public AutomaticMongoRepositoryConfiguration(String interfaceName,
SimpleMongoRepositoryConfiguration parent) {
super(interfaceName, parent);
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.document.mongodb.repository.config.
* SimpleMongoRepositoryConfiguration
* .MongoRepositoryConfiguration#getMongoTemplateRef()
*/
public String getMongoTemplateRef() {
return getParent().getMongoTemplateRef();
}
} }
}
} }

View File

@@ -0,0 +1,487 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.convert;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.document.mongodb.mapping.MappingException;
import org.springframework.data.document.mongodb.mapping.MappingIntrospector;
import org.springframework.data.document.mongodb.mapping.MongoMappingContext;
import org.springframework.data.mapping.annotation.Id;
import org.springframework.data.mapping.annotation.Persistent;
import org.springframework.data.mapping.annotation.Transient;
import org.springframework.data.mapping.annotation.Value;
import org.springframework.data.mapping.model.PersistentEntity;
import org.springframework.data.mapping.model.PersistentProperty;
import org.springframework.data.mapping.model.types.Association;
import org.springframework.data.mapping.model.types.Simple;
import org.springframework.data.mapping.reflect.ClassPropertyFetcher;
import org.springframework.data.mapping.reflect.ReflectionUtils;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@SuppressWarnings({"unchecked"})
public class MappingMongoConverter implements MongoConverter, ApplicationContextAware {
protected static final Log log = LogFactory.getLog(MappingMongoConverter.class);
protected static final Set<String> SIMPLE_TYPES;
protected static final ConcurrentMap<Class<?>, Map<String, Field>> fieldsByName = new ConcurrentHashMap<Class<?>, Map<String, Field>>();
static {
Set<String> basics = new HashSet<String>();
basics.add(boolean.class.getName());
basics.add(long.class.getName());
basics.add(short.class.getName());
basics.add(int.class.getName());
basics.add(byte.class.getName());
basics.add(float.class.getName());
basics.add(double.class.getName());
basics.add(char.class.getName());
basics.add(Boolean.class.getName());
basics.add(Long.class.getName());
basics.add(Short.class.getName());
basics.add(Integer.class.getName());
basics.add(Byte.class.getName());
basics.add(Float.class.getName());
basics.add(Double.class.getName());
basics.add(Character.class.getName());
basics.add(String.class.getName());
basics.add(java.util.Date.class.getName());
basics.add(Locale.class.getName());
basics.add(Class.class.getName());
basics.add(DBRef.class.getName());
basics.add(Pattern.class.getName());
basics.add(CodeWScope.class.getName());
basics.add(ObjectId.class.getName());
// TODO check on enums..
basics.add(Enum.class.getName());
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
}
protected GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
protected MongoMappingContext mappingContext;
protected ApplicationContext applicationContext;
protected boolean autowirePersistentBeans = false;
public MappingMongoConverter() {
initializeConverters();
}
public MappingMongoConverter(MongoMappingContext mappingContext) {
this.mappingContext = mappingContext;
initializeConverters();
}
public MappingMongoConverter(MongoMappingContext mappingContext, List<Converter<?, ?>> converters) {
this.mappingContext = mappingContext;
if (null != converters) {
for (Converter<?, ?> c : converters) {
conversionService.addConverter(c);
}
}
initializeConverters();
}
public MongoMappingContext getMappingContext() {
return mappingContext;
}
public void setMappingContext(MongoMappingContext mappingContext) {
this.mappingContext = mappingContext;
}
public boolean isAutowirePersistentBeans() {
return autowirePersistentBeans;
}
public void setAutowirePersistentBeans(boolean autowirePersistentBeans) {
this.autowirePersistentBeans = autowirePersistentBeans;
}
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
return conversionService.convert(id, targetType);
}
public ObjectId convertObjectId(Object id) {
return conversionService.convert(id, ObjectId.class);
}
public <S extends Object> S read(Class<S> clazz, final DBObject dbo) {
final StandardEvaluationContext ctx = new StandardEvaluationContext();
if (null != applicationContext) {
ctx.setBeanResolver(new BeanFactoryResolver(applicationContext));
}
String[] keySet = dbo.keySet().toArray(new String[]{});
for (String key : keySet) {
ctx.setVariable(key, dbo.get(key));
}
try {
if ((clazz.isArray() || clazz.isAssignableFrom(Collection.class)) && dbo instanceof BasicDBList) {
List l = new ArrayList<S>();
BasicDBList dbList = (BasicDBList) dbo;
for (Object o : dbList) {
if (o instanceof DBObject) {
Object newObj = read(clazz.getComponentType(), (DBObject) o);
if (newObj.getClass().isAssignableFrom(clazz.getComponentType())) {
l.add(newObj);
} else {
l.add(conversionService.convert(newObj, clazz.getComponentType()));
}
} else {
l.add(o);
}
}
return conversionService.convert(l, clazz);
}
final MappingIntrospector<S> introspector = MappingIntrospector.getInstance(clazz);
final List<String> cparamNames = new ArrayList<String>();
final S instance = introspector.createInstance(new MappingIntrospector.ParameterValueProvider() {
public <T> T getParameterValue(String name, Class<T> type, Expression spelExpr) {
Object o = getValueInternal(name, type, dbo, ctx, spelExpr);
if (null != o && o.getClass().isAssignableFrom(type)) {
cparamNames.add(name);
return (T) o;
} else {
cparamNames.add(name);
return conversionService.convert(o, type);
}
}
});
introspector.doWithProperties(new MappingIntrospector.PropertyHandler() {
public void doWithProperty(PropertyDescriptor descriptor, Field field, Expression spelExpr) {
String name = descriptor.getName();
if (!cparamNames.contains(name)) {
Object o = getValueInternal(name, descriptor.getPropertyType(), dbo, ctx, spelExpr);
try {
introspector.setValue(name, instance, o);
} catch (MappingException e) {
log.error(e.getMessage(), e);
}
}
}
});
introspector.maybeAutowire(instance, applicationContext, autowirePersistentBeans);
return instance;
} catch (MappingException e) {
throw new RuntimeException(e);
}
}
public void write(final Object o, final DBObject dbo) {
try {
final MappingIntrospector<?> introspector = MappingIntrospector.getInstance(o.getClass());
Field idFld = introspector.getIdField();
if (null != idFld) {
Object idObj = introspector.getFieldValue(idFld.getName(), o);
if (null != idObj) {
dbo.put("_id", conversionService.convert(idObj, ObjectId.class));
}
}
introspector.doWithProperties(new MappingIntrospector.PropertyHandler() {
public void doWithProperty(PropertyDescriptor descriptor, Field field, Expression spelExpr) {
String name = descriptor.getName();
try {
Object newObj = introspector.getFieldValue(name, o);
if (null != newObj) {
if (MappingIntrospector.isSimpleType(newObj.getClass())) {
dbo.put(name, newObj);
} else {
if (newObj.getClass().isAssignableFrom(Collection.class)) {
BasicDBList dbList = new BasicDBList();
write(newObj, dbList);
dbo.put(name, dbList);
} else {
DBObject newDbObj = new BasicDBObject();
write(newObj, newDbObj);
dbo.put(name, newDbObj);
}
}
}
} catch (MappingException e) {
log.error(e.getMessage(), e);
}
}
});
// Handle mapping-specific stuff
if (introspector.isMappable()) {
introspector.doWithAssociations(new MappingIntrospector.AssociationHandler() {
public void doWithAssociation(MappingIntrospector.Association association) {
log.info("HANDLE ASSOCIATION: " + association);
}
});
}
} catch (MappingException e) {
log.error(e.getMessage(), e);
}
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
protected boolean isTransientField(Field f) {
return (Modifier.isTransient(f.getModifiers()) || null != f.getAnnotation(Transient.class) || null != f.getAnnotation(Autowired.class));
}
protected boolean isPersistentProperty(Object obj, PropertyDescriptor descriptor) {
try {
Field f = obj.getClass().getDeclaredField(descriptor.getName());
return false;
} catch (NoSuchFieldException e) {
return false;
}
}
protected void initializeConverters() {
if (!conversionService.canConvert(ObjectId.class, String.class)) {
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
}
if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) {
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
conversionService.addConverter(BigIntegerToIdConverter.INSTANCE);
}
if (!conversionService.canConvert(ObjectId.class, Integer.class)) {
conversionService.addConverter(IntegerToIdConverter.INSTANCE);
conversionService.addConverter(IdToIntegerConverter.INSTANCE);
}
if (!conversionService.canConvert(Object.class, DBObject.class)) {
conversionService.addConverter(new ObjectToDBObjectConverter());
}
}
protected Object getValueInternal(String name, Class<?> type, DBObject dbo, StandardEvaluationContext ctx, Expression spelExpr) {
Object o;
if (null != spelExpr) {
o = spelExpr.getValue(ctx);
} else {
Object dbObj = dbo.get(name);
if (dbObj instanceof DBObject) {
// It's a complex object, have to read it in
o = read(type, (DBObject) dbObj);
} else {
o = dbObj;
}
}
return o;
}
protected static boolean isSimpleType(Class<?> propertyType) {
if (propertyType == null) {
return false;
}
if (propertyType.isArray()) {
return isSimpleType(propertyType.getComponentType());
}
return SIMPLE_TYPES.contains(propertyType.getName());
}
/**
* Simple singleton to convert {@link ObjectId}s to their {@link String} representation.
*
* @author Oliver Gierke
*/
public static enum ObjectIdToStringConverter implements Converter<ObjectId, String> {
INSTANCE;
public String convert(ObjectId id) {
return id.toString();
}
}
/**
* Simple singleton to convert {@link String}s to their {@link ObjectId} representation.
*
* @author Oliver Gierke
*/
public static enum StringToObjectIdConverter implements Converter<String, ObjectId> {
INSTANCE;
public ObjectId convert(String source) {
return new ObjectId(source);
}
}
/**
* Simple singleton to convert {@link ObjectId}s to their {@link java.math.BigInteger} representation.
*
* @author Oliver Gierke
*/
public static enum ObjectIdToBigIntegerConverter implements Converter<ObjectId, BigInteger> {
INSTANCE;
public BigInteger convert(ObjectId source) {
return new BigInteger(source.toString(), 16);
}
}
/**
* Simple singleton to convert {@link BigInteger}s to their {@link ObjectId} representation.
*
* @author Oliver Gierke
*/
public static enum BigIntegerToIdConverter implements Converter<BigInteger, ObjectId> {
INSTANCE;
public ObjectId convert(BigInteger source) {
return new ObjectId(source.toString(16));
}
}
public static enum IntegerToIdConverter implements Converter<Integer, ObjectId> {
INSTANCE;
public ObjectId convert(Integer source) {
String s = String.valueOf(source);
StringBuffer buff = new StringBuffer();
for (int i = 0; i < (16 - s.length()); i++) {
buff.append("0");
}
buff.append(s);
return new ObjectId(buff.toString());
}
}
public static enum IdToIntegerConverter implements Converter<ObjectId, Integer> {
INSTANCE;
public Integer convert(ObjectId source) {
return Integer.parseInt(source.toString());
}
}
protected class ObjectToDBObjectConverter implements Converter<Object, DBObject> {
private final DBObject dbo;
public ObjectToDBObjectConverter() {
this.dbo = null;
}
public ObjectToDBObjectConverter(DBObject dbo) {
this.dbo = dbo;
}
public DBObject convert(Object source) {
DBObject dbo;
if (source instanceof Collection) {
Collection c = (Collection) source;
dbo = (null == this.dbo ? new BasicDBList() : this.dbo);
for (Object o : c) {
if (isSimpleType(o.getClass())) {
((BasicDBList) dbo).add(o);
} else {
((BasicDBList) dbo).add(convert(o));
}
}
} else if (source instanceof Map) {
Map<Object, Object> m = (Map) source;
dbo = (null == this.dbo ? new BasicDBObject() : this.dbo);
for (Map.Entry<Object, Object> entry : m.entrySet()) {
String key = (entry.getKey() instanceof String ? entry.getKey().toString() : conversionService.convert(entry.getKey(), String.class));
if (isSimpleType(entry.getValue().getClass())) {
dbo.put(key, entry.getValue());
} else {
dbo.put(key, convert(entry.getValue()));
}
}
} else {
dbo = (null == this.dbo ? new BasicDBObject() : this.dbo);
if (!fieldsByName.containsKey(source.getClass())) {
Map<String, Field> fields = new HashMap<String, Field>();
for (Field f : source.getClass().getDeclaredFields()) {
if (!"class".equals(f.getName())) {
ReflectionUtils.makeAccessible(f);
fields.put(f.getName(), f);
}
}
fieldsByName.put(source.getClass(), fields);
}
try {
for (PropertyDescriptor descriptor : Introspector.getBeanInfo(source.getClass()).getPropertyDescriptors()) {
Field f = fieldsByName.get(source.getClass()).get(descriptor.getName());
if (null != f && !isTransientField(f)) {
try {
Object o;
if (null != descriptor.getReadMethod()) {
o = descriptor.getReadMethod().invoke(source);
} else {
o = f.get(source);
}
if (null != o && isSimpleType(o.getClass())) {
dbo.put(descriptor.getName(), o);
} else if (null != o) {
dbo.put(descriptor.getName(), convert(o));
} else {
// Value was NULL, skip it
}
} catch (InvocationTargetException e) {
throw new RuntimeException("Error converting " + source + " to DBObject");
} catch (IllegalAccessException e) {
throw new RuntimeException("Error converting " + source + " to DBObject");
}
}
}
} catch (IntrospectionException e) {
throw new RuntimeException("Error converting " + source + " to DBObject");
}
}
return dbo;
}
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.data.document.mongodb; package org.springframework.data.document.mongodb.convert;
import static org.springframework.beans.PropertyAccessorFactory.*; import static org.springframework.beans.PropertyAccessorFactory.*;
@@ -21,6 +21,7 @@ import org.springframework.beans.BeanWrapper;
import org.springframework.beans.ConfigurablePropertyAccessor; import org.springframework.beans.ConfigurablePropertyAccessor;
import org.springframework.beans.NotWritablePropertyException; import org.springframework.beans.NotWritablePropertyException;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.data.document.mongodb.MongoPropertyDescriptors;
import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor; import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor;
import org.springframework.util.Assert; import org.springframework.util.Assert;

View File

@@ -13,29 +13,31 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.data.document.mongodb; package org.springframework.data.document.mongodb.convert;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.springframework.data.document.mongodb.MongoReader;
import org.springframework.data.document.mongodb.MongoWriter;
public interface MongoConverter extends MongoWriter<Object>, MongoReader<Object> { public interface MongoConverter extends MongoWriter<Object>, MongoReader<Object> {
/** /**
* Converts the given {@link ObjectId} to the given target type. * Converts the given {@link ObjectId} to the given target type.
* *
* @param <T> the actual type to create * @param <T> the actual type to create
* @param id the source {@link ObjectId} * @param id the source {@link ObjectId}
* @param targetType the target type to convert the {@link ObjectId} to * @param targetType the target type to convert the {@link ObjectId} to
* @return * @return
*/ */
public <T> T convertObjectId(ObjectId id, Class<T> targetType); public <T> T convertObjectId(ObjectId id, Class<T> targetType);
/** /**
* Returns the {@link ObjectId} instance for the given id. * Returns the {@link ObjectId} instance for the given id.
* *
* @param id * @param id
* @return * @return
*/ */
public ObjectId convertObjectId(Object id); public ObjectId convertObjectId(Object id);
} }

View File

@@ -0,0 +1,546 @@
/*
* Copyright 2010-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.convert;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.document.mongodb.MongoPropertyDescriptors.MongoPropertyDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.comparator.CompoundComparator;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
/**
* Basic {@link MongoConverter} implementation to convert between domain classes and {@link DBObject}s.
*
* @author Mark Pollack
* @author Thomas Risberg
* @author Oliver Gierke
*/
public class SimpleMongoConverter implements MongoConverter {
private static final Log LOG = LogFactory.getLog(SimpleMongoConverter.class);
private static final Set<String> SIMPLE_TYPES;
static {
Set<String> basics = new HashSet<String>();
basics.add(boolean.class.getName());
basics.add(long.class.getName());
basics.add(short.class.getName());
basics.add(int.class.getName());
basics.add(byte.class.getName());
basics.add(float.class.getName());
basics.add(double.class.getName());
basics.add(char.class.getName());
basics.add(Boolean.class.getName());
basics.add(Long.class.getName());
basics.add(Short.class.getName());
basics.add(Integer.class.getName());
basics.add(Byte.class.getName());
basics.add(Float.class.getName());
basics.add(Double.class.getName());
basics.add(Character.class.getName());
basics.add(String.class.getName());
basics.add(java.util.Date.class.getName());
// basics.add(Time.class.getName());
// basics.add(Timestamp.class.getName());
// basics.add(java.sql.Date.class.getName());
// basics.add(BigDecimal.class.getName());
// basics.add(BigInteger.class.getName());
basics.add(Locale.class.getName());
// basics.add(Calendar.class.getName());
// basics.add(GregorianCalendar.class.getName());
// basics.add(java.util.Currency.class.getName());
// basics.add(TimeZone.class.getName());
// basics.add(Object.class.getName());
basics.add(Class.class.getName());
// basics.add(byte[].class.getName());
// basics.add(Byte[].class.getName());
// basics.add(char[].class.getName());
// basics.add(Character[].class.getName());
// basics.add(Blob.class.getName());
// basics.add(Clob.class.getName());
// basics.add(Serializable.class.getName());
// basics.add(URI.class.getName());
// basics.add(URL.class.getName());
basics.add(DBRef.class.getName());
basics.add(Pattern.class.getName());
basics.add(CodeWScope.class.getName());
basics.add(ObjectId.class.getName());
// TODO check on enums..
basics.add(Enum.class.getName());
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
}
private final GenericConversionService conversionService;
/**
* Creates a {@link SimpleMongoConverter}.
*/
public SimpleMongoConverter() {
this.conversionService = ConversionServiceFactory.createDefaultConversionService();
initializeConverters();
}
/**
* Creates a new {@link SimpleMongoConverter} for the given {@link ConversionService}.
*
* @param conversionService
*/
public SimpleMongoConverter(GenericConversionService conversionService) {
Assert.notNull(conversionService);
this.conversionService = conversionService;
initializeConverters();
}
/**
* Initializes additional converters that handle {@link ObjectId} conversion. Will register converters for supported
* id types if none are registered for those conversion already. {@link GenericConversionService} is configured.
*/
protected void initializeConverters() {
if (!conversionService.canConvert(ObjectId.class, String.class)) {
conversionService.addConverter(ObjectIdToStringConverter.INSTANCE);
conversionService.addConverter(StringToObjectIdConverter.INSTANCE);
}
if (!conversionService.canConvert(ObjectId.class, BigInteger.class)) {
conversionService.addConverter(ObjectIdToBigIntegerConverter.INSTANCE);
conversionService.addConverter(BigIntegerToIdConverter.INSTANCE);
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.document.mongodb.MongoWriter#write(java.lang.Object, com.mongodb.DBObject)
*/
@SuppressWarnings("rawtypes")
public void write(Object obj, DBObject dbo) {
MongoBeanWrapper beanWrapper = createWrapper(obj, false);
for (MongoPropertyDescriptor descriptor : beanWrapper.getDescriptors()) {
if (descriptor.isMappable()) {
Object value = beanWrapper.getValue(descriptor);
if (value == null) {
continue;
}
String keyToUse = descriptor.getKeyToMap();
// TODO validate Enums...
if (descriptor.isEnum()) {
writeValue(dbo, keyToUse, ((Enum) value).name());
} else if (descriptor.isIdProperty() && descriptor.isOfIdType()) {
if (value instanceof String && ObjectId.isValid((String) value)) {
try {
writeValue(dbo, keyToUse, conversionService.convert(value, ObjectId.class));
} catch (ConversionFailedException iae) {
LOG.warn("Unable to convert the String " + value + " to an ObjectId");
writeValue(dbo, keyToUse, value);
}
} else {
// we can't convert this id - use as is
writeValue(dbo, keyToUse, value);
}
} else {
writeValue(dbo, keyToUse, value);
}
} else {
if (!"class".equals(descriptor.getName())) {
LOG.warn("Unable to map property " + descriptor.getName() + ". Skipping.");
}
}
}
}
/**
* Writes the given value to the given {@link DBObject}. Will skip {@literal null} values.
*
* @param dbo
* @param keyToUse
* @param value
*/
private void writeValue(DBObject dbo, String keyToUse, Object value) {
if (!isSimpleType(value.getClass())) {
writeCompoundValue(dbo, keyToUse, value);
} else {
dbo.put(keyToUse, value);
}
}
/**
* Writes the given {@link CompoundComparator} value to the given {@link DBObject}.
*
* @param dbo
* @param keyToUse
* @param value
*/
@SuppressWarnings("unchecked")
private void writeCompoundValue(DBObject dbo, String keyToUse, Object value) {
if (value instanceof Map) {
writeMap(dbo, keyToUse, (Map<String, Object>) value);
return;
}
if (value instanceof Collection) {
// Should write a collection!
writeArray(dbo, keyToUse, ((Collection<Object>) value).toArray());
return;
}
if (value instanceof Object[]) {
// Should write an array!
writeArray(dbo, keyToUse, (Object[]) value);
return;
}
DBObject nestedDbo = new BasicDBObject();
write(value, nestedDbo);
dbo.put(keyToUse, nestedDbo);
}
/**
* Writes the given {@link Map} to the given {@link DBObject}.
*
* @param dbo
* @param mapKey
* @param map
*/
protected void writeMap(DBObject dbo, String mapKey, Map<String, Object> map) {
// TODO support non-string based keys as long as there is a Spring Converter obj->string and (optionally)
// string->obj
DBObject dboToPopulate = null;
// TODO - Does that make sense? If we create a new object here it's content will never make it out of this
// method
if (mapKey != null) {
dboToPopulate = new BasicDBObject();
} else {
dboToPopulate = dbo;
}
if (map != null) {
for (Entry<String, Object> entry : map.entrySet()) {
Object entryValue = entry.getValue();
String entryKey = entry.getKey();
if (!isSimpleType(entryValue.getClass())) {
writeCompoundValue(dboToPopulate, entryKey, entryValue);
} else {
dboToPopulate.put(entryKey, entryValue);
}
}
dbo.put(mapKey, dboToPopulate);
}
}
/**
* Writes the given array to the given {@link DBObject}.
*
* @param dbo
* @param keyToUse
* @param array
*/
protected void writeArray(DBObject dbo, String keyToUse, Object[] array) {
// TODO
Object[] dboValues;
if (array != null) {
dboValues = new Object[array.length];
int i = 0;
for (Object o : array) {
if (!isSimpleType(o.getClass())) {
DBObject dboValue = new BasicDBObject();
write(o, dboValue);
dboValues[i] = dboValue;
} else {
dboValues[i] = o;
}
i++;
}
dbo.put(keyToUse, dboValues);
}
}
/*
* (non-Javadoc)
*
* @see org.springframework.data.document.mongodb.MongoReader#read(java.lang.Class, com.mongodb.DBObject)
*/
public <S> S read(Class<S> clazz, DBObject source) {
if (source == null) {
return null;
}
Assert.notNull(clazz, "Mapped class was not specified");
S target = BeanUtils.instantiateClass(clazz);
MongoBeanWrapper bw = new MongoBeanWrapper(target, conversionService, true);
for (MongoPropertyDescriptor descriptor : bw.getDescriptors()) {
String keyToUse = descriptor.getKeyToMap();
if (source.containsField(keyToUse)) {
if (descriptor.isMappable()) {
Object value = source.get(keyToUse);
if (!isSimpleType(value.getClass())) {
if (value instanceof Object[]) {
bw.setValue(descriptor, readCollection(descriptor, Arrays.asList((Object[]) value))
.toArray());
} else if (value instanceof BasicDBList) {
bw.setValue(descriptor, readCollection(descriptor, (BasicDBList) value));
} else if (value instanceof DBObject) {
bw.setValue(descriptor, readCompoundValue(descriptor, (DBObject) value));
} else {
LOG.warn("Unable to map compound DBObject field " + keyToUse + " to property "
+ descriptor.getName()
+ ". The field value should have been a 'DBObject.class' but was "
+ value.getClass().getName());
}
} else {
bw.setValue(descriptor, value);
}
} else {
LOG.warn("Unable to map DBObject field " + keyToUse + " to property " + descriptor.getName()
+ ". Skipping.");
}
}
}
return target;
}
/**
* Reads the given collection values (that are {@link DBObject}s potentially) into a {@link Collection} of domain
* objects.
*
* @param descriptor
* @param values
* @return
*/
private Collection<Object> readCollection(MongoPropertyDescriptor descriptor, Collection<?> values) {
Class<?> targetCollectionType = descriptor.getPropertyType();
boolean targetIsArray = targetCollectionType.isArray();
@SuppressWarnings("unchecked")
Collection<Object> result = targetIsArray ? new ArrayList<Object>(values.size()) : CollectionFactory
.createCollection(targetCollectionType, values.size());
for (Object o : values) {
if (o instanceof DBObject) {
Class<?> type;
if (targetIsArray) {
type = targetCollectionType.getComponentType();
} else {
type = getGenericParameters(descriptor.getTypeToSet()).get(0);
}
result.add(read(type, (DBObject) o));
} else {
result.add(o);
}
}
return result;
}
/**
* Reads a compound value from the given {@link DBObject} for the given property.
*
* @param pd
* @param dbo
* @return
*/
private Object readCompoundValue(MongoPropertyDescriptor pd, DBObject dbo) {
Assert.isTrue(!pd.isCollection(), "Collections not supported!");
if (pd.isMap()) {
return readMap(pd, dbo, getGenericParameters(pd.getTypeToSet()).get(1));
} else {
return read(pd.getPropertyType(), dbo);
}
}
/**
* Create a {@link Map} instance. Will return a {@link HashMap} by default. Subclasses might want to override this
* method to use a custom {@link Map} implementation.
*
* @return
*/
protected Map<String, Object> createMap() {
return new HashMap<String, Object>();
}
/**
* Reads every key/value pair from the {@link DBObject} into a {@link Map} instance.
*
* @param pd
* @param dbo
* @param targetType
* @return
*/
protected Map<?, ?> readMap(MongoPropertyDescriptor pd, DBObject dbo, Class<?> targetType) {
Map<String, Object> map = createMap();
for (String key : dbo.keySet()) {
Object value = dbo.get(key);
if (!isSimpleType(value.getClass())) {
map.put(key, read(targetType, (DBObject) value));
// Can do some reflection tricks here -
// throw new RuntimeException("User types not supported yet as values for Maps");
} else {
map.put(key, conversionService.convert(value, targetType));
}
}
return map;
}
protected static boolean isSimpleType(Class<?> propertyType) {
if (propertyType == null) {
return false;
}
if (propertyType.isArray()) {
return isSimpleType(propertyType.getComponentType());
}
return SIMPLE_TYPES.contains(propertyType.getName());
}
/**
* Callback to allow customizing creation of a {@link MongoBeanWrapper}.
*
* @param target the target object to wrap
* @param fieldAccess whether to use field access or property access
* @return
*/
protected MongoBeanWrapper createWrapper(Object target, boolean fieldAccess) {
return new MongoBeanWrapper(target, conversionService, fieldAccess);
}
public List<Class<?>> getGenericParameters(Type genericParameterType) {
List<Class<?>> actualGenericParameterTypes = new ArrayList<Class<?>>();
if (genericParameterType instanceof ParameterizedType) {
ParameterizedType aType = (ParameterizedType) genericParameterType;
Type[] parameterArgTypes = aType.getActualTypeArguments();
for (Type parameterArgType : parameterArgTypes) {
if (parameterArgType instanceof GenericArrayType) {
Class<?> arrayType = (Class<?>) ((GenericArrayType) parameterArgType).getGenericComponentType();
actualGenericParameterTypes.add(Array.newInstance(arrayType, 0).getClass());
} else {
if (parameterArgType instanceof ParameterizedType) {
ParameterizedType paramTypeArgs = (ParameterizedType) parameterArgType;
actualGenericParameterTypes.add((Class<?>) paramTypeArgs.getRawType());
} else {
if (parameterArgType instanceof TypeVariable) {
throw new RuntimeException("Can not map " + ((TypeVariable<?>) parameterArgType).getName());
} else {
if (parameterArgType instanceof Class) {
actualGenericParameterTypes.add((Class<?>) parameterArgType);
} else {
throw new RuntimeException("Can not map " + parameterArgType);
}
}
}
}
}
}
return actualGenericParameterTypes;
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.convert.MongoConverter#convertObjectId(org.bson.types.ObjectId, java.lang.Class)
*/
public <T> T convertObjectId(ObjectId id, Class<T> targetType) {
return conversionService.convert(id, targetType);
}
/* (non-Javadoc)
* @see org.springframework.data.document.mongodb.convert.MongoConverter#convertObjectId(java.lang.Object)
*/
public ObjectId convertObjectId(Object id) {
return conversionService.convert(id, ObjectId.class);
}
/**
* Simple singleton to convert {@link ObjectId}s to their {@link String} representation.
*
* @author Oliver Gierke
*/
public static enum ObjectIdToStringConverter implements Converter<ObjectId, String> {
INSTANCE;
public String convert(ObjectId id) {
return id.toString();
}
}
/**
* Simple singleton to convert {@link String}s to their {@link ObjectId} representation.
*
* @author Oliver Gierke
*/
public static enum StringToObjectIdConverter implements Converter<String, ObjectId> {
INSTANCE;
public ObjectId convert(String source) {
return new ObjectId(source);
}
}
/**
* Simple singleton to convert {@link ObjectId}s to their {@link BigInteger} representation.
*
* @author Oliver Gierke
*/
public static enum ObjectIdToBigIntegerConverter implements Converter<ObjectId, BigInteger> {
INSTANCE;
public BigInteger convert(ObjectId source) {
return new BigInteger(source.toString(), 16);
}
}
/**
* Simple singleton to convert {@link BigInteger}s to their {@link ObjectId} representation.
*
* @author Oliver Gierke
*/
public static enum BigIntegerToIdConverter implements Converter<BigInteger, ObjectId> {
INSTANCE;
public ObjectId convert(BigInteger source) {
return new ObjectId(source.toString(16));
}
}
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.convert;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.mapping.annotation.Transient;
import org.springframework.data.mapping.reflect.ReflectionUtils;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class SimplePojoDBObjectConverter {
protected static final Set<String> SIMPLE_TYPES;
static {
Set<String> basics = new HashSet<String>();
basics.add(boolean.class.getName());
basics.add(long.class.getName());
basics.add(short.class.getName());
basics.add(int.class.getName());
basics.add(byte.class.getName());
basics.add(float.class.getName());
basics.add(double.class.getName());
basics.add(char.class.getName());
basics.add(Boolean.class.getName());
basics.add(Long.class.getName());
basics.add(Short.class.getName());
basics.add(Integer.class.getName());
basics.add(Byte.class.getName());
basics.add(Float.class.getName());
basics.add(Double.class.getName());
basics.add(Character.class.getName());
basics.add(String.class.getName());
basics.add(java.util.Date.class.getName());
basics.add(Locale.class.getName());
basics.add(Class.class.getName());
basics.add(DBRef.class.getName());
basics.add(Pattern.class.getName());
basics.add(CodeWScope.class.getName());
basics.add(ObjectId.class.getName());
// TODO check on enums..
basics.add(Enum.class.getName());
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
}
protected static final ConcurrentMap<Class<?>, Map<String, Field>> fieldsByName = new ConcurrentHashMap<Class<?>, Map<String, Field>>();
protected GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
public SimplePojoDBObjectConverter() {
}
public GenericConversionService getConversionService() {
return conversionService;
}
public void setConversionService(GenericConversionService conversionService) {
this.conversionService = conversionService;
}
public static boolean isSimpleType(Class<?> propertyType) {
if (propertyType == null) {
return false;
}
if (propertyType.isArray()) {
return isSimpleType(propertyType.getComponentType());
}
return SIMPLE_TYPES.contains(propertyType.getName());
}
public static boolean isTransientField(Field f) {
return (Modifier.isTransient(f.getModifiers()) || null != f.getAnnotation(Transient.class) || null != f.getAnnotation(Autowired.class));
}
public DBObject convert(Object source, DBObject existing) throws Exception {
DBObject dbo;
if (source instanceof Collection) {
Collection c = (Collection) source;
dbo = (null == existing ? new BasicDBList() : existing);
for (Object o : c) {
if (isSimpleType(o.getClass())) {
((BasicDBList) dbo).add(o);
} else {
((BasicDBList) dbo).add(convert(o, null));
}
}
} else if (source instanceof Map) {
Map<Object, Object> m = (Map) source;
dbo = (null == existing ? new BasicDBObject() : existing);
for (Map.Entry<Object, Object> entry : m.entrySet()) {
String key = (entry.getKey() instanceof String ? entry.getKey().toString() : conversionService.convert(entry.getKey(), String.class));
if (isSimpleType(entry.getValue().getClass())) {
dbo.put(key, entry.getValue());
} else {
dbo.put(key, convert(entry.getValue(), null));
}
}
} else {
dbo = (null == existing ? new BasicDBObject() : existing);
if (!fieldsByName.containsKey(source.getClass())) {
Map<String, Field> fields = new HashMap<String, Field>();
for (Field f : source.getClass().getDeclaredFields()) {
if (!"class".equals(f.getName())) {
ReflectionUtils.makeAccessible(f);
fields.put(f.getName(), f);
}
}
fieldsByName.put(source.getClass(), fields);
}
try {
for (PropertyDescriptor descriptor : Introspector.getBeanInfo(source.getClass()).getPropertyDescriptors()) {
Field f = fieldsByName.get(source.getClass()).get(descriptor.getName());
if (null != f && !isTransientField(f)) {
try {
Object o;
if (null != descriptor.getReadMethod()) {
o = descriptor.getReadMethod().invoke(source);
} else {
o = f.get(source);
}
if (null != o && isSimpleType(o.getClass())) {
dbo.put(descriptor.getName(), o);
} else if (null != o) {
dbo.put(descriptor.getName(), convert(o, null));
} else {
// Value was NULL, skip it
}
} catch (InvocationTargetException e) {
throw new RuntimeException("Error converting " + source + " to DBObject");
} catch (IllegalAccessException e) {
throw new RuntimeException("Error converting " + source + " to DBObject");
}
}
}
} catch (IntrospectionException e) {
throw new RuntimeException("Error converting " + source + " to DBObject");
}
}
return dbo;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.index;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CompoundIndex {
String def();
boolean unique() default false;
boolean sparse() default false;
boolean dropDups() default true;
String name() default "";
String collection() default "";
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.index;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CompoundIndexes {
CompoundIndex[] value();
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.index;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public interface IndexDefinition<T> {
T getIndexDefinition();
T getIndexOptions();
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.index;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public enum IndexDirection {
ASCENDING,
DESCENDING;
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.index;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public abstract class IndexPredicate {
private String name;
private IndexDirection direction = IndexDirection.ASCENDING;
private boolean unique = false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public IndexDirection getDirection() {
return direction;
}
public void setDirection(IndexDirection direction) {
this.direction = direction;
}
public boolean isUnique() {
return unique;
}
public void setUnique(boolean unique) {
this.unique = unique;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.index;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Indexed {
boolean unique() default false;
IndexDirection direction() default IndexDirection.ASCENDING;
boolean sparse() default false;
boolean dropDups() default true;
String name() default "";
String collection() default "";
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MappingException extends Throwable {
private final Object source;
public MappingException(Throwable throwable, Object source) {
super(String.format("Error encountered mapping object: %s", source), throwable);
this.source = source;
}
public Object getSource() {
return source;
}
}

View File

@@ -0,0 +1,417 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import com.mongodb.DBRef;
import com.sun.org.apache.xalan.internal.extensions.ExpressionContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.types.CodeWScope;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.mapping.annotation.*;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MappingIntrospector<T> {
private static final Log log = LogFactory.getLog(MappingIntrospector.class);
private static final ConcurrentMap<Class<?>, MappingIntrospector<?>> introspectors = new ConcurrentHashMap<Class<?>, MappingIntrospector<?>>();
private static final Set<String> SIMPLE_TYPES;
static {
Set<String> basics = new HashSet<String>();
basics.add(boolean.class.getName());
basics.add(long.class.getName());
basics.add(short.class.getName());
basics.add(int.class.getName());
basics.add(byte.class.getName());
basics.add(float.class.getName());
basics.add(double.class.getName());
basics.add(char.class.getName());
basics.add(Boolean.class.getName());
basics.add(Long.class.getName());
basics.add(Short.class.getName());
basics.add(Integer.class.getName());
basics.add(Byte.class.getName());
basics.add(Float.class.getName());
basics.add(Double.class.getName());
basics.add(Character.class.getName());
basics.add(String.class.getName());
basics.add(java.util.Date.class.getName());
basics.add(Locale.class.getName());
basics.add(Class.class.getName());
basics.add(DBRef.class.getName());
basics.add(Pattern.class.getName());
basics.add(CodeWScope.class.getName());
basics.add(ObjectId.class.getName());
// TODO check on enums..
basics.add(Enum.class.getName());
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
}
private final Class<T> clazz;
private final BeanInfo beanInfo;
private final Map<String, PropertyDescriptor> properties;
private final Map<String, Field> fields;
private final Set<Association> associations;
private final Map<String, Method> setters;
private final Map<String, Method> getters;
private Field idField = null;
private List<String> ignoredProperties = new ArrayList<String>() {{
add("class");
}};
private SpelExpressionParser parser = new SpelExpressionParser();
private Map<String, Expression> expressions = new HashMap<String, Expression>();
private PreferredConstructor preferredConstructor = null;
@SuppressWarnings({"unchecked"})
private MappingIntrospector(Class<T> clazz) throws MappingException {
this.clazz = clazz;
try {
this.beanInfo = Introspector.getBeanInfo(clazz);
} catch (IntrospectionException e) {
throw new MappingException(e, clazz);
}
Map<String, PropertyDescriptor> properties = new HashMap<String, PropertyDescriptor>();
Map<String, Field> fields = new HashMap<String, Field>();
Set<Association> associations = new HashSet<Association>();
Map<String, Method> setters = new HashMap<String, Method>();
Map<String, Method> getters = new HashMap<String, Method>();
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
String name = descriptor.getName();
if (!ignoredProperties.contains(name)) {
try {
Field fld = clazz.getDeclaredField(name);
Class<?> fldType = fld.getType();
fld.setAccessible(true);
if (!isTransientField(fld)) {
if (fld.isAnnotationPresent(Id.class)) {
if (null != idField) {
throw new IllegalStateException("You cannot have two fields in a domain object annotated with Id! " + clazz);
}
idField = fld;
continue;
} else if (null == idField && fldType.equals(ObjectId.class)) {
// Respect fields of the MongoDB ObjectId type
idField = fld;
continue;
} else if (null == idField
&& (fldType.equals(String.class) || fldType.equals(BigInteger.class))
&& ("id".equals(name) || "_id".equals(name))) {
// Strings and BigIntegers named "id"|"_id" are also valid ID fields
idField = fld;
continue;
} else if (fld.isAnnotationPresent(OneToMany.class)
|| fld.isAnnotationPresent(OneToOne.class)
|| fld.isAnnotationPresent(ManyToMany.class)) {
associations.add(new Association(descriptor, fld));
continue;
} else if (fld.isAnnotationPresent(Value.class)) {
// @Value fields are evaluated at runtime and are the same transient fields
continue;
}
fields.put(name, fld);
properties.put(name, descriptor);
setters.put(name, descriptor.getWriteMethod());
getters.put(name, descriptor.getReadMethod());
if (fld.isAnnotationPresent(Value.class)) {
expressions.put(name, parser.parseExpression(fld.getAnnotation(Value.class).value()));
}
}
} catch (NoSuchFieldException e) {
log.warn(e.getMessage());
}
}
}
this.properties = Collections.unmodifiableMap(properties);
this.fields = Collections.unmodifiableMap(fields);
this.associations = Collections.unmodifiableSet(associations);
this.setters = Collections.unmodifiableMap(setters);
this.getters = Collections.unmodifiableMap(getters);
// Find the right constructor
for (Constructor<?> constructor : clazz.getConstructors()) {
if (constructor.getParameterTypes().length != 0) {
// Non-no-arg constructor
if (null == preferredConstructor) {
String[] paramNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(constructor);
Type[] paramTypes = constructor.getGenericParameterTypes();
Class<?>[] paramClassTypes = new Class[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
Class<?> targetType;
if (paramTypes[i] instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) paramTypes[i];
Type[] types = ptype.getActualTypeArguments();
if (types.length == 1) {
targetType = (Class<?>) types[0];
} else {
targetType = (Class<?>) ptype.getRawType();
}
} else {
targetType = (Class<?>) paramTypes[i];
}
paramClassTypes[i] = targetType;
}
preferredConstructor = new PreferredConstructor((Constructor<T>) constructor, paramNames, paramClassTypes);
}
}
}
}
@SuppressWarnings({"unchecked"})
public static <C extends Object> MappingIntrospector<C> getInstance(Class<C> clazz) throws MappingException {
MappingIntrospector<C> introspector = (MappingIntrospector<C>) introspectors.get(clazz);
if (null == introspector) {
introspector = new MappingIntrospector<C>(clazz);
introspectors.put(clazz, introspector);
}
return introspector;
}
public Class<T> getTargetClass() {
return clazz;
}
public Field getField(String name) {
return fields.get(name);
}
public PropertyDescriptor getPropertyDescriptor(String name) {
return properties.get(name);
}
public boolean isMappable() {
return (null != clazz.getAnnotation(Persistent.class));
}
public Field getIdField() {
return idField;
}
public T createInstance() throws MappingException {
return createInstance(null);
}
public T createInstance(ParameterValueProvider provider) throws MappingException {
try {
if (null == preferredConstructor || null == provider) {
return clazz.newInstance();
} else {
List<Object> params = new LinkedList<Object>();
for (ConstructorParameter<?> p : preferredConstructor.parameters) {
Expression x = null;
Value v = p.getValueAnnotation();
if (null != v) {
x = parser.parseExpression(v.value());
}
params.add(provider.getParameterValue(p.name, p.type, x));
}
return preferredConstructor.constructor.newInstance(params.toArray());
}
} catch (InvocationTargetException e) {
throw new MappingException(e, clazz);
} catch (InstantiationException e) {
throw new MappingException(e, clazz);
} catch (IllegalAccessException e) {
throw new MappingException(e, clazz);
}
}
public Object getFieldValue(PropertyDescriptor descriptor, Object from) throws MappingException {
try {
Method getter = descriptor.getReadMethod();
if (null != getter) {
return getter.invoke(from);
} else {
Field f = fields.get(descriptor.getName());
return f.get(from);
}
} catch (IllegalAccessException e) {
throw new MappingException(e, from);
} catch (InvocationTargetException e) {
throw new MappingException(e, from);
}
}
public Object getFieldValue(String name, Object from) throws MappingException {
PropertyDescriptor descriptor = properties.get(name);
if (null != descriptor) {
return getFieldValue(descriptor, from);
}
return null;
}
public void setValue(String name, Object on, Object value) throws MappingException {
Field f = fields.get(name);
if (null != f) {
Method setter = setters.get(name);
try {
if (null != setter) {
setter.invoke(on, value);
} else {
f.set(on, value);
}
} catch (IllegalAccessException e) {
throw new MappingException(e, value);
} catch (InvocationTargetException e) {
throw new MappingException(e, value);
}
}
}
public void maybeAutowire(Object obj, ApplicationContext applicationContext, boolean autowire) throws MappingException {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(obj);
evaluationContext.setBeanResolver(new BeanFactoryResolver(applicationContext));
if (autowire) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(obj);
if (obj instanceof InitializingBean) {
try {
((InitializingBean) obj).afterPropertiesSet();
} catch (Exception e) {
throw new MappingException(e, obj);
}
}
}
for (Map.Entry<String, Expression> entry : expressions.entrySet()) {
setValue(entry.getKey(), obj, entry.getValue().getValue(evaluationContext));
}
}
public void doWithProperties(PropertyHandler handler) {
for (Map.Entry<String, PropertyDescriptor> entry : properties.entrySet()) {
String name = entry.getKey();
handler.doWithProperty(entry.getValue(), fields.get(name), expressions.get(name));
}
}
public void doWithAssociations(AssociationHandler handler) {
for (Association association : associations) {
handler.doWithAssociation(association);
}
}
public static boolean isSimpleType(Class<?> propertyType) {
if (propertyType == null) {
return false;
}
if (propertyType.isArray()) {
return isSimpleType(propertyType.getComponentType());
}
return SIMPLE_TYPES.contains(propertyType.getName());
}
public static boolean isTransientField(Field f) {
return (Modifier.isTransient(f.getModifiers()) || null != f.getAnnotation(Transient.class) || null != f.getAnnotation(Autowired.class));
}
public static class Association {
private final PropertyDescriptor descriptor;
private final Field field;
public Association(PropertyDescriptor descriptor, Field field) {
this.descriptor = descriptor;
this.field = field;
}
public PropertyDescriptor getDescriptor() {
return descriptor;
}
public Field getField() {
return field;
}
}
private class PreferredConstructor {
Constructor<T> constructor;
ConstructorParameter<?>[] parameters;
@SuppressWarnings({"unchecked"})
public PreferredConstructor(Constructor<T> constructor, String[] names, Class<?>[] types) {
this.constructor = constructor;
Annotation[][] annos = constructor.getParameterAnnotations();
parameters = new ConstructorParameter[names.length];
for (int i = 0; i < names.length; i++) {
parameters[i] = new ConstructorParameter(names[i], types[i], annos[i]);
}
}
}
private class ConstructorParameter<V> {
String name;
Class<V> type;
Annotation[] annotations;
private ConstructorParameter(String name, Class<V> type, Annotation[] annotations) {
this.name = name;
this.type = type;
this.annotations = annotations;
}
private Value getValueAnnotation() {
for (Annotation anno : annotations) {
if (anno instanceof Value) {
return (Value) anno;
}
}
return null;
}
}
public interface AssociationHandler {
void doWithAssociation(Association association);
}
public interface PropertyHandler {
void doWithProperty(PropertyDescriptor descriptor, Field field, Expression spelExpr);
}
public interface ParameterValueProvider {
<T> T getParameterValue(String name, Class<T> type, Expression spelExpr);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import org.springframework.data.mapping.model.AbstractClassMapping;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
public class MongoClassMapping extends AbstractClassMapping<MongoCollection> {
public MongoClassMapping(PersistentEntity entity, MappingContext context) {
super(entity, context);
}
@Override
public MongoCollection getMappedForm() {
return (MongoCollection) context.getMappingFactory().createMappedForm(entity);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoCollection {
private String name;
public MongoCollection(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,457 @@
package org.springframework.data.document.mongodb.mapping;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.util.JSON;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.index.CompoundIndex;
import org.springframework.data.document.mongodb.index.CompoundIndexes;
import org.springframework.data.document.mongodb.index.IndexDirection;
import org.springframework.data.document.mongodb.index.Indexed;
import org.springframework.data.document.mongodb.CollectionCallback;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.mapping.annotation.*;
import org.springframework.data.mapping.model.*;
import org.springframework.data.mapping.model.types.Association;
import org.springframework.data.mapping.reflect.ClassPropertyFetcher;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
public class MongoMappingConfigurationStrategy implements MappingConfigurationStrategy {
protected MongoTemplate mongo;
protected MongoMappingFactory mappingFactory;
protected SpelExpressionParser expressionParser = new SpelExpressionParser();
protected TemplateParserContext templateParserContext = new TemplateParserContext();
protected Map<Class<?>, PersistenceDescriptor> descriptors = new ConcurrentHashMap<Class<?>, PersistenceDescriptor>();
protected Map<Class<?>, Set<PersistentEntity>> owners = new LinkedHashMap<Class<?>, Set<PersistentEntity>>();
public MongoMappingConfigurationStrategy(MongoTemplate mongo) {
this.mongo = mongo;
}
public MongoMappingConfigurationStrategy(MongoTemplate mongo, MongoMappingFactory mappingFactory) {
this.mongo = mongo;
this.mappingFactory = mappingFactory;
}
public SpelExpressionParser getExpressionParser() {
return expressionParser;
}
public void setExpressionParser(SpelExpressionParser expressionParser) {
this.expressionParser = expressionParser;
}
public TemplateParserContext getTemplateParserContext() {
return templateParserContext;
}
public void setTemplateParserContext(TemplateParserContext templateParserContext) {
this.templateParserContext = templateParserContext;
}
@SuppressWarnings({"unchecked"})
public boolean isPersistentEntity(Class aClass) {
if (null != aClass) {
return (null != aClass.getAnnotation(Persistent.class));
}
return false;
}
public List<PersistentProperty> getPersistentProperties(Class aClass, MappingContext mappingContext) {
return getPersistentProperties(aClass, mappingContext, null);
}
public List<PersistentProperty> getPersistentProperties(Class aClass,
MappingContext mappingContext,
ClassMapping classMapping) {
PersistenceDescriptor pd = getPersistenceDescriptor(aClass, mappingContext, classMapping);
return (null != pd ? pd.getProperties() : null);
}
public PersistentProperty getIdentity(Class aClass, MappingContext mappingContext) {
PersistenceDescriptor idPd = getPersistenceDescriptor(aClass, mappingContext, null);
return (null != idPd ? idPd.getIdProperty() : null);
}
public IdentityMapping getDefaultIdentityMapping(final ClassMapping classMapping) {
final PersistentProperty<?> prop = getPersistenceDescriptor(classMapping.getEntity().getJavaClass(),
classMapping.getEntity().getMappingContext(),
classMapping).getIdProperty();
return new IdentityMapping() {
public String[] getIdentifierName() {
return new String[]{prop.getName()};
}
public ClassMapping getClassMapping() {
return classMapping;
}
public Object getMappedForm() {
return classMapping.getMappedForm();
}
};
}
public Set getOwningEntities(Class aClass, MappingContext mappingContext) {
return owners.get(aClass);
}
protected PersistenceDescriptor getPersistenceDescriptor(Class<?> javaClass,
MappingContext context,
ClassMapping mapping) {
PersistenceDescriptor descriptor = descriptors.get(javaClass);
if (null == descriptor) {
ClassPropertyFetcher fetcher = ClassPropertyFetcher.forClass(javaClass);
PersistentEntity entity = getPersistentEntity(javaClass, context, mapping);
String collection = javaClass.getSimpleName().toLowerCase();
for (Annotation anno : javaClass.getAnnotations()) {
if (anno instanceof Persistent) {
} else if (anno instanceof CompoundIndexes) {
CompoundIndexes idxs = (CompoundIndexes) anno;
for (CompoundIndex idx : idxs.value()) {
String idxColl = collection;
if (!"".equals(idx.collection())) {
idxColl = idx.collection();
}
String name = null;
if (!"".equals(idx.name())) {
name = idx.name();
}
ensureIndex(idxColl, name, idx.def(), null, idx.unique(), idx.dropDups(), idx.sparse());
}
}
}
EvaluationContext elContext = createElContext();
elContext.setVariable("class", javaClass);
PersistentProperty<?> id = extractIdProperty(entity, context, elContext);
List<PersistentProperty> properties = new LinkedList<PersistentProperty>();
for (PropertyDescriptor propertyDescriptor : fetcher.getPropertyDescriptors()) {
if (null == id || !propertyDescriptor.getName().equals(id.getName())) {
PersistentProperty<?> p = createPersistentProperty(entity, context, propertyDescriptor, mapping);
if (null != p) {
properties.add(p);
for (Annotation anno : fetcher.getDeclaredField(p.getName()).getDeclaredAnnotations()) {
if (anno instanceof Indexed) {
Indexed idx = (Indexed) anno;
String idxColl = collection;
if (!"".equals(idx.collection())) {
idxColl = idx.collection();
}
String name = p.getName();
if (!"".equals(idx.name())) {
name = idx.name();
}
ensureIndex(idxColl, name, null, idx.direction(), idx.unique(), idx.dropDups(), idx.sparse());
}
}
}
}
}
descriptor = new PersistenceDescriptor(entity, id, properties);
descriptors.put(javaClass, descriptor);
}
return descriptor;
}
protected PersistentEntity getPersistentEntity(Class<?> javaClass, MappingContext context, ClassMapping mapping) {
Assert.notNull(javaClass);
if (null != mapping) {
return mapping.getEntity();
} else {
return context.getPersistentEntity(javaClass.getName());
}
}
protected PersistentProperty<?> createPersistentProperty(PersistentEntity entity,
MappingContext mappingContext,
PropertyDescriptor descriptor,
ClassMapping mapping) {
ClassPropertyFetcher fetcher = ClassPropertyFetcher.forClass(entity.getJavaClass());
Field f = fetcher.getDeclaredField(descriptor.getName());
if (null != f) {
// Handle associations and persistent types
PersistentProperty<?> prop = extractChildType(entity, mappingContext, descriptor, mapping);
if (null == prop) {
// Must be a simple type
prop = mappingFactory.createSimple(entity, mappingContext, descriptor);
}
return prop;
}
return null;
}
protected PersistentProperty<?> extractChildType(PersistentEntity entity,
MappingContext mappingContext,
PropertyDescriptor descriptor,
ClassMapping mapping) {
ClassPropertyFetcher fetcher = ClassPropertyFetcher.forClass(entity.getJavaClass());
Field f = fetcher.getDeclaredField(descriptor.getName());
Class<?> childClass = null;
Association<?> assoc = null;
if (f.getType().isAssignableFrom(List.class)) {
if (null != f.getAnnotation(org.springframework.data.mapping.annotation.OneToMany.class)) {
org.springframework.data.mapping.annotation.OneToMany otm = f.getAnnotation(
org.springframework.data.mapping.annotation.OneToMany.class);
if (Object.class != otm.targetClass()) {
childClass = otm.targetClass();
} else {
childClass = extractType(f);
}
assoc = mappingFactory.createOneToMany(entity, mappingContext, descriptor);
} else if (null != f.getAnnotation(org.springframework.data.mapping.annotation.ManyToMany.class)) {
org.springframework.data.mapping.annotation.ManyToMany mtm = f.getAnnotation(
org.springframework.data.mapping.annotation.ManyToMany.class);
if (Object.class != mtm.targetClass()) {
childClass = mtm.targetClass();
} else {
childClass = extractType(f);
}
assoc = mappingFactory.createManyToMany(entity, mappingContext, descriptor);
}
} else {
if (null != f.getAnnotation(OneToOne.class) || null != f.getType().getAnnotation(Persistent.class)) {
childClass = f.getType();
assoc = mappingFactory.createOneToOne(entity, mappingContext, descriptor);
}
}
if (null != childClass && null != assoc) {
if (childClass != entity.getJavaClass()) {
PersistentEntity childEntity = getPersistentEntity(childClass, mappingContext, mapping);
addOwner(entity.getJavaClass(), childEntity);
assoc.setAssociatedEntity(childEntity);
} else {
addOwner(entity.getJavaClass(), entity);
assoc.setAssociatedEntity(entity);
}
}
return assoc;
}
protected Class<?> extractType(Field f) {
Type t = f.getGenericType();
if (null != t && t instanceof ParameterizedType) {
Type[] types = ((ParameterizedType) t).getActualTypeArguments();
return ((Class) types[0]);
}
return f.getType();
}
protected IdentifiedBy extractIdentifiedBy(Class<?> clazz, Field field) {
Persistent cAnno = clazz.getAnnotation(Persistent.class);
Persistent fldAnno = (null != field ? field.getAnnotation(Persistent.class) : null);
return (null != fldAnno ? fldAnno.identifiedBy() : cAnno.identifiedBy());
}
protected PersistenceStrategy extractPersistenceStrategy(Class<?> clazz, Field field) {
Persistent cAnno = clazz.getAnnotation(Persistent.class);
Persistent fldAnno = (null != field ? field.getAnnotation(Persistent.class) : null);
return (null != fldAnno ? fldAnno.strategy() : cAnno.strategy());
}
protected String extractId(Class<?> clazz, Field field) {
Persistent cAnno = clazz.getAnnotation(Persistent.class);
Persistent fldAnno = (null != field ? field.getAnnotation(Persistent.class) : null);
return (null != fldAnno ? fldAnno.id() : cAnno.id());
}
protected void addOwner(Class<?> javaClass, PersistentEntity parent) {
if (!owners.containsKey(javaClass)) {
Set<PersistentEntity> owningEntities = new HashSet<PersistentEntity>();
owningEntities.add(parent);
owners.put(javaClass, owningEntities);
}
}
protected PersistentProperty<?> extractIdProperty(PersistentEntity entity,
MappingContext mappingContext,
EvaluationContext elContext) {
ClassPropertyFetcher fetcher = ClassPropertyFetcher.forClass(entity.getJavaClass());
// Let field annotation override that on the class
IdentifiedBy idBy = extractIdentifiedBy(fetcher.getJavaClass(), null);
String id = extractId(fetcher.getJavaClass(), null);
Assert.notNull(id);
if (id.indexOf("#") > -1) {
id = expressionParser.parseExpression(id).getValue(elContext, String.class);
}
PropertyDescriptor idPropDesc = null;
switch (idBy) {
case DEFAULT:
idPropDesc = findIdByAnnotation(fetcher);
if (null == idPropDesc) {
idPropDesc = fetcher.getPropertyDescriptor(id);
}
break;
case ANNOTATION:
idPropDesc = findIdByAnnotation(fetcher);
break;
case PROPERTY:
idPropDesc = fetcher.getPropertyDescriptor(id);
break;
case VALUE:
try {
idPropDesc = new ValuePropertyDescriptor("id", entity.getJavaClass(), id);
} catch (IntrospectionException e) {
throw new IllegalStateException(e.getMessage(), e);
}
break;
}
//Assert.notNull(idPropDesc, String.format("No ID property could be found on the entity %s", entity));
return (null != idPropDesc ? mappingFactory.createIdentity(entity, mappingContext, idPropDesc) : null);
}
protected PropertyDescriptor findIdByAnnotation(ClassPropertyFetcher fetcher) {
for (PropertyDescriptor descriptor : fetcher.getPropertyDescriptors()) {
Field f = fetcher.getDeclaredField(descriptor.getName());
if (null != f && null != f.getAnnotation(Id.class)) {
return descriptor;
}
}
return null;
}
protected EvaluationContext createElContext() {
StandardEvaluationContext elContext = new StandardEvaluationContext(System.getProperties());
for (String prop : System.getProperties().stringPropertyNames()) {
elContext.setVariable(prop, System.getProperty(prop));
}
return elContext;
}
protected void ensureIndex(String collection,
final String name,
final String def,
final IndexDirection direction,
final boolean unique,
final boolean dropDups,
final boolean sparse) {
mongo.execute(collection, new CollectionCallback<Object>() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
DBObject defObj;
if (null != def) {
defObj = (DBObject) JSON.parse(def);
} else {
defObj = new BasicDBObject();
defObj.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
}
DBObject opts = new BasicDBObject();
if (!"".equals(name)) {
opts.put("name", name);
}
opts.put("dropDups", dropDups);
opts.put("sparse", sparse);
opts.put("unique", unique);
collection.ensureIndex(defObj, opts);
return null;
}
});
}
protected class PersistenceDescriptor {
PersistentEntity entity;
PersistentProperty idProperty = null;
List<PersistentProperty> properties = null;
PersistenceDescriptor(PersistentEntity entity, PersistentProperty idProperty, List<PersistentProperty> properties) {
this.entity = entity;
this.idProperty = idProperty;
this.properties = properties;
}
public PersistentEntity getEntity() {
return entity;
}
public PersistentProperty getIdProperty() {
return idProperty;
}
public List<PersistentProperty> getProperties() {
return properties;
}
}
protected class ValuePropertyDescriptor extends PropertyDescriptor {
Object value;
ValuePropertyDescriptor(String s, Class<?> aClass, Object value) throws IntrospectionException {
super(s, aClass);
this.value = value;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
@Override
public Method getReadMethod() {
try {
return getClass().getMethod("getValue", null);
} catch (NoSuchMethodException e) {
// IGNORED
}
return super.getReadMethod();
}
@Override
public void setReadMethod(Method method) throws IntrospectionException {
// IGNORED
}
@Override
public Method getWriteMethod() {
try {
return getClass().getMethod("setValue", Object.class);
} catch (NoSuchMethodException e) {
// IGNORED
}
return super.getWriteMethod();
}
@Override
public void setWriteMethod(Method method) throws IntrospectionException {
// IGNORED
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.mapping.model.AbstractMappingContext;
import org.springframework.data.mapping.model.MappingConfigurationStrategy;
import org.springframework.data.mapping.model.MappingFactory;
import org.springframework.data.mapping.model.PersistentEntity;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoMappingContext extends AbstractMappingContext {
private MongoTemplate mongo;
private MongoMappingFactory mappingFactory = null;
private MappingConfigurationStrategy strategy = null;
public MongoMappingContext(MongoTemplate mongo) {
this.mongo = mongo;
this.mappingFactory = new MongoMappingFactory();
strategy = new MongoMappingConfigurationStrategy(mongo, mappingFactory);
}
public MongoMappingContext(MongoTemplate mongo, MongoMappingFactory mappingFactory) {
this.mongo = mongo;
this.mappingFactory = mappingFactory;
strategy = new MongoMappingConfigurationStrategy(mongo, mappingFactory);
}
@Override
protected PersistentEntity createPersistentEntity(Class javaClass) {
MongoPersistentEntity entity = new MongoPersistentEntity(javaClass, this);
return entity;
}
public MappingConfigurationStrategy getMappingSyntaxStrategy() {
return strategy;
}
public MappingFactory getMappingFactory() {
return mappingFactory;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.mapping.model.MappingFactory;
import org.springframework.data.mapping.model.PersistentEntity;
import org.springframework.data.mapping.model.PersistentProperty;
import org.springframework.data.mapping.reflect.ClassPropertyFetcher;
import java.lang.reflect.Field;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoMappingFactory extends MappingFactory<MongoCollection, MongoProperty> {
@Override
public MongoCollection createMappedForm(PersistentEntity entity) {
String name = entity.getJavaClass().getSimpleName().toLowerCase();
return new MongoCollection(name);
}
@Override
public MongoProperty createMappedForm(PersistentProperty property) {
String name = property.getName();
return new MongoProperty(name, false);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import org.springframework.data.mapping.model.AbstractPersistentEntity;
import org.springframework.data.mapping.model.ClassMapping;
import org.springframework.data.mapping.model.MappingContext;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoPersistentEntity extends AbstractPersistentEntity {
@SuppressWarnings({"unchecked"})
public MongoPersistentEntity(Class<?> javaClass, MappingContext context) {
super(javaClass, context);
}
@SuppressWarnings({"unchecked"})
public ClassMapping<MongoCollection> getMapping() {
return new MongoClassMapping(this, getMappingContext());
}
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoProperty {
private String name;
public MongoProperty(String name, boolean indexed) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping.index;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import org.springframework.data.document.mongodb.index.IndexDefinition;
import org.springframework.data.document.mongodb.index.IndexDirection;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class MongoIndexDefinition implements IndexDefinition<DBObject> {
private String collection = null;
private String name = null;
private IndexDirection direction = IndexDirection.ASCENDING;
private boolean unique = false;
private boolean dropDups = true;
private boolean sparse = false;
private String definition = null;
public MongoIndexDefinition() {
}
public String getCollection() {
return collection;
}
public void setCollection(String collection) {
this.collection = collection;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public IndexDirection getDirection() {
return direction;
}
public void setDirection(IndexDirection direction) {
this.direction = direction;
}
public boolean isUnique() {
return unique;
}
public void setUnique(boolean unique) {
this.unique = unique;
}
public boolean isDropDups() {
return dropDups;
}
public void setDropDups(boolean dropDups) {
this.dropDups = dropDups;
}
public boolean isSparse() {
return sparse;
}
public void setSparse(boolean sparse) {
this.sparse = sparse;
}
public String getDefinition() {
return definition;
}
public void setDefinition(String definition) {
this.definition = definition;
}
public DBObject getIndexDefinition() {
DBObject dbo;
if (null != definition) {
dbo = (DBObject) JSON.parse(definition);
} else {
dbo = new BasicDBObject();
dbo.put(name, (direction == IndexDirection.ASCENDING ? 1 : -1));
}
return dbo;
}
public DBObject getIndexOptions() {
DBObject dbo = new BasicDBObject();
dbo.put("dropDups", dropDups);
dbo.put("sparse", sparse);
dbo.put("unique", unique);
return dbo;
}
}

View File

@@ -23,7 +23,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.springframework.data.document.mongodb.MongoConverter; import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.MongoTemplate; import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.query.Query; import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;

View File

@@ -55,7 +55,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
* *
* @see * @see
* org.springframework.data.document.mongodb.repository.AbstractMongoQuery#createQuery(org.springframework.data. * org.springframework.data.document.mongodb.repository.AbstractMongoQuery#createQuery(org.springframework.data.
* repository.query.SimpleParameterAccessor, org.springframework.data.document.mongodb.MongoConverter) * repository.query.SimpleParameterAccessor, org.springframework.data.document.mongodb.support.convert.MongoConverter)
*/ */
@Override @Override
protected Query createQuery(ConvertingParameterAccessor accessor) { protected Query createQuery(ConvertingParameterAccessor accessor) {

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.annotation;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public @interface Aliases {
String[] value();
}

View File

@@ -0,0 +1,14 @@
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface Id {
}

View File

@@ -0,0 +1,12 @@
package org.springframework.data.mapping.annotation;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
public enum IdentifiedBy {
DEFAULT,
ANNOTATION,
PROPERTY,
VALUE,
PARENT
}

View File

@@ -0,0 +1,17 @@
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface ManyToMany {
Class<?> targetClass() default Object.class;
}

View File

@@ -0,0 +1,17 @@
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface OneToMany {
Class<?> targetClass() default Object.class;
}

View File

@@ -0,0 +1,14 @@
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface OneToOne {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
public @interface PersistenceConstructor {
}

View File

@@ -0,0 +1,11 @@
package org.springframework.data.mapping.annotation;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
public enum PersistenceStrategy {
DEFAULT,
KEY_VALUE,
DOCUMENT,
SERIALIZED
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD})
public @interface Persistent {
PersistenceStrategy strategy() default PersistenceStrategy.DOCUMENT;
IdentifiedBy identifiedBy() default IdentifiedBy.DEFAULT;
String id() default "id";
}

View File

@@ -0,0 +1,14 @@
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author J. Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Transient {
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.PARAMETER,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE
})
public @interface Value {
String value();
}

View File

@@ -0,0 +1,42 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* Abstract implementation of the ClassMapping interface
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class AbstractClassMapping<T> implements ClassMapping{
protected PersistentEntity entity;
protected MappingContext context;
public AbstractClassMapping(PersistentEntity entity, MappingContext context) {
super();
this.entity = entity;
this.context = context;
}
public PersistentEntity getEntity() {
return this.entity;
}
public abstract T getMappedForm();
public IdentityMapping getIdentifier() {
return context.getMappingSyntaxStrategy().getDefaultIdentityMapping(this);
}
}

View File

@@ -0,0 +1,156 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.validation.Validator;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Abstract implementation of the MappingContext interface
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class AbstractMappingContext implements MappingContext {
protected Collection<PersistentEntity> persistentEntities = new ConcurrentLinkedQueue<PersistentEntity>();
protected Map<String, PersistentEntity> persistentEntitiesByName = new ConcurrentHashMap<String, PersistentEntity>();
protected Map<PersistentEntity, Map<String, PersistentEntity>> persistentEntitiesByDiscriminator = new ConcurrentHashMap<PersistentEntity, Map<String, PersistentEntity>>();
protected Map<PersistentEntity, Validator> entityValidators = new ConcurrentHashMap<PersistentEntity, Validator>();
protected Collection<Listener> eventListeners = new ConcurrentLinkedQueue<Listener>();
protected GenericConversionService conversionService = new GenericConversionService();
public ConversionService getConversionService() {
return conversionService;
}
public ConverterRegistry getConverterRegistry() {
return conversionService;
}
public void addMappingContextListener(Listener listener) {
if (listener != null)
eventListeners.add(listener);
}
public void addTypeConverter(Converter converter) {
conversionService.addConverter(converter);
}
public Validator getEntityValidator(PersistentEntity entity) {
if (entity != null) {
return entityValidators.get(entity);
}
return null;
}
public void addEntityValidator(PersistentEntity entity, Validator validator) {
if (entity != null && validator != null) {
entityValidators.put(entity, validator);
}
}
public PersistentEntity addExternalPersistentEntity(Class javaClass) {
if (javaClass == null) throw new IllegalArgumentException("PersistentEntity class cannot be null");
PersistentEntity entity = persistentEntitiesByName.get(javaClass.getName());
if (entity == null) {
entity = addPersistentEntityInternal(javaClass, true);
}
return entity;
}
public final PersistentEntity addPersistentEntity(Class javaClass) {
if (javaClass == null) throw new IllegalArgumentException("PersistentEntity class cannot be null");
PersistentEntity entity = persistentEntitiesByName.get(javaClass.getName());
if (entity == null) {
entity = addPersistentEntityInternal(javaClass, false);
}
return entity;
}
private PersistentEntity addPersistentEntityInternal(Class javaClass, boolean isExternal) {
PersistentEntity entity;
entity = createPersistentEntity(javaClass);
entity.setExternal(isExternal);
persistentEntities.remove(entity);
persistentEntities.add(entity);
persistentEntitiesByName.put(entity.getName(), entity);
try {
entity.afterPropertiesSet();
} catch (Exception e) {
throw new RuntimeException(String.format("Error initializing PersistentEntity: %s", entity), e);
}
if (!entity.isRoot()) {
PersistentEntity root = entity.getRootEntity();
Map<String, PersistentEntity> children = persistentEntitiesByDiscriminator.get(root);
if (children == null) {
children = new ConcurrentHashMap<String, PersistentEntity>();
persistentEntitiesByDiscriminator.put(root, children);
}
children.put(entity.getDiscriminator(), entity);
}
for (Listener eventListener : eventListeners) {
eventListener.persistentEntityAdded(entity);
}
return entity;
}
public PersistentEntity getChildEntityByDiscriminator(PersistentEntity root, String discriminator) {
final Map<String, PersistentEntity> children = persistentEntitiesByDiscriminator.get(root);
if (children != null) {
return children.get(discriminator);
}
return null;
}
protected abstract PersistentEntity createPersistentEntity(Class javaClass);
public Collection<PersistentEntity> getPersistentEntities() {
return persistentEntities;
}
public boolean isPersistentEntity(Class type) {
return type != null && getPersistentEntity(type.getName()) != null;
}
public boolean isPersistentEntity(Object value) {
return value != null && isPersistentEntity(value.getClass());
}
public PersistentEntity getPersistentEntity(String name) {
final int proxyIndicator = name.indexOf("_$$_");
if (proxyIndicator > -1) {
name = name.substring(0, proxyIndicator);
}
return persistentEntitiesByName.get(name);
}
}

View File

@@ -0,0 +1,193 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mapping.model.types.Association;
import org.springframework.data.mapping.model.types.OneToMany;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Abstract implementation to be subclasses on a per datastore basis
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class AbstractPersistentEntity<T> implements PersistentEntity<T> {
protected Class<T> javaClass;
protected List<PersistentProperty> persistentProperties;
protected List<Association> associations;
protected Map<String, PersistentProperty> propertiesByName = new HashMap<String, PersistentProperty>();
protected MappingContext context;
protected PersistentProperty identity;
protected List<String> persistentPropertyNames;
private String decapitalizedName;
protected Set owners;
private PersistentEntity parentEntity = null;
private boolean external;
public AbstractPersistentEntity(Class<T> javaClass, MappingContext context) {
if (javaClass == null) throw new IllegalArgumentException("The argument [javaClass] cannot be null");
this.javaClass = javaClass;
this.context = context;
this.decapitalizedName = Introspector.decapitalize(javaClass.getSimpleName());
}
public boolean isExternal() {
return external;
}
public void setExternal(boolean external) {
this.external = external;
}
public MappingContext getMappingContext() {
return this.context;
}
public void afterPropertiesSet() throws Exception {
this.identity = context.getMappingSyntaxStrategy().getIdentity(javaClass, context);
this.owners = context.getMappingSyntaxStrategy().getOwningEntities(javaClass, context);
this.persistentProperties = context.getMappingSyntaxStrategy().getPersistentProperties(javaClass, context);
persistentPropertyNames = new ArrayList<String>();
associations = new ArrayList();
for (PersistentProperty persistentProperty : persistentProperties) {
if (!(persistentProperty instanceof OneToMany))
persistentPropertyNames.add(persistentProperty.getName());
if (persistentProperty instanceof Association) {
associations.add((Association) persistentProperty);
}
}
for (PersistentProperty persistentProperty : persistentProperties) {
propertiesByName.put(persistentProperty.getName(), persistentProperty);
}
Class<?> superClass = javaClass.getSuperclass();
if (superClass != null
&& !superClass.equals(Object.class)
&& !Modifier.isAbstract(superClass.getModifiers())) {
this.parentEntity = context.addPersistentEntity(superClass);
}
getMapping().getMappedForm(); // initialize mapping
}
public boolean hasProperty(String name, Class type) {
final PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(getJavaClass(), name);
return pd != null && pd.getPropertyType().equals(type);
}
public boolean isIdentityName(String propertyName) {
return getIdentity().getName().equals(propertyName);
}
public PersistentEntity getParentEntity() {
return parentEntity;
}
public String getDiscriminator() {
return getJavaClass().getSimpleName();
}
public PersistentEntity getRootEntity() {
PersistentEntity root = this;
PersistentEntity parent = getParentEntity();
while (parent != null) {
root = parent;
parent = parent.getParentEntity();
}
return root;
}
public boolean isRoot() {
return getParentEntity() == null;
}
public boolean isOwningEntity(PersistentEntity owner) {
return owner != null && owners.contains(owner.getJavaClass());
}
public String getDecapitalizedName() {
return this.decapitalizedName;
}
public List<String> getPersistentPropertyNames() {
return this.persistentPropertyNames;
}
public T newInstance() {
try {
return getJavaClass().newInstance();
} catch (InstantiationException e) {
throw new EntityInstantiationException("Unable to create entity of type [" + getJavaClass() + "]: " + e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new EntityInstantiationException("Unable to create entity of type [" + getJavaClass() + "]: " + e.getMessage(), e);
}
}
public String getName() {
return javaClass.getName();
}
public PersistentProperty getIdentity() {
return this.identity;
}
public Class<T> getJavaClass() {
return this.javaClass;
}
public boolean isInstance(Object obj) {
return getJavaClass().isInstance(obj);
}
public List<PersistentProperty> getPersistentProperties() {
return persistentProperties;
}
public List<Association> getAssociations() {
return associations;
}
public PersistentProperty getPropertyByName(String name) {
return propertiesByName.get(name);
}
@Override
public int hashCode() {
return javaClass.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof PersistentEntity)) return false;
if (this == o) return true;
PersistentEntity other = (PersistentEntity) o;
return javaClass.equals(other.getJavaClass());
}
@Override
public String toString() {
return "PersistentEntity[" + javaClass.getName() + "]";
}
}

View File

@@ -0,0 +1,77 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import java.beans.PropertyDescriptor;
/**
* Abstract implementation of the PersistentProperty interface that
* uses the PropertyDescriptor instance to establish name and type
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class AbstractPersistentProperty implements PersistentProperty {
protected PersistentEntity owner;
protected MappingContext context;
protected String name;
protected Class type;
private boolean nullable = false;
public AbstractPersistentProperty(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
this.owner = owner;
this.context = context;
this.name = descriptor.getName();
this.type = descriptor.getPropertyType();
}
public AbstractPersistentProperty(PersistentEntity owner, MappingContext context, String name, Class type) {
this.owner = owner;
this.context = context;
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public String getCapitilizedName() {
String name = getName();
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
public Class getType() {
return type;
}
public PersistentEntity getOwner() {
return owner;
}
@Override
public String toString() {
return getName() + ":" + getType().getName();
}
public boolean isNullable() {
return nullable;
}
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
}

View File

@@ -0,0 +1,31 @@
package org.springframework.data.mapping.model;
/**
* A class mapping is a mapping between a class and some external
* form such as a table, column family, or document (depending on the underlying data store)
*
* @author Graeme Rocher
* @since 1.0
*/
public interface ClassMapping<T> {
/**
* Obtains the PersistentEntity for this class mapping
*
* @return The PersistentEntity
*/
PersistentEntity getEntity();
/**
* Returns the mapped form of the class such as a Table, a Key Space, Document etc.
*
* @return The mapped representation
*/
T getMappedForm();
/**
* Returns details of the identifier used for this class
*
* @return The Identity
*/
IdentityMapping getIdentifier();
}

View File

@@ -0,0 +1,18 @@
package org.springframework.data.mapping.model;
/**
* Exception thrown when something goes wrong configuring a datastore
*
* @author Graeme Rocher
* @since 1.0
*/
public class DatastoreConfigurationException extends RuntimeException{
public DatastoreConfigurationException(String message) {
super(message);
}
public DatastoreConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* Created by IntelliJ IDEA.
* User: jbrisbin
* Date: 2/25/11
* Time: 9:07 AM
* To change this template use File | Settings | File Templates.
*/
public class EntityInstantiationException extends RuntimeException {
public EntityInstantiationException(String s, Throwable throwable) {
super(s, throwable);
}
}

View File

@@ -0,0 +1,31 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* @author Graeme Rocher
* @since 1.0
*/
public interface IdentityMapping extends PropertyMapping{
/**
* The identifier property name(s) as an array. Usually there is just one identifier
* name, however in the case of a composite or natural identifier there
* may be serveral names
*
* @return A String[] of identifier names that make up the key
*/
String[] getIdentifierName();
}

View File

@@ -0,0 +1,28 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
/**
* Thrown when an error occurs reading the mapping between object and datastore
*
* @author Graeme Rocher
* @since 1.0
*/
public class IllegalMappingException extends RuntimeException {
public IllegalMappingException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,70 @@
package org.springframework.data.mapping.model;
import java.util.List;
import java.util.Set;
/**
* <p>This interface defines a strategy for reading how
* persistent properties are defined in a persistent entity.</p>
* <p/>
* <p>Subclasses can implement this interface in order to provide
* a different mechanism for mapping entities such as annotations or XML.</p>
*
* @author Graeme Rocher
* @since 1.0
*/
public interface MappingConfigurationStrategy {
/**
* Tests whether the given class is a persistent entity
*
* @param javaClass The java class
* @return true if it is a persistent entity
*/
<T> boolean isPersistentEntity(Class<T> javaClass);
/**
* @see #getPersistentProperties(Class, MappingContext, ClassMapping)
*/
<T> List<PersistentProperty> getPersistentProperties(Class<T> javaClass, MappingContext context);
/**
* Obtains a List of PersistentProperty instances for the given Mapped class
*
* @param javaClass The Java class
* @param context The MappingContext instance
* @param mapping The mapping for this class
* @return The PersistentProperty instances
*/
<T> List<PersistentProperty> getPersistentProperties(Class<T> javaClass, MappingContext context, ClassMapping mapping);
/**
* Obtains the identity of a persistent entity
*
* @param javaClass The Java class
* @param context The MappingContext
* @return A PersistentProperty instance
*/
<T> PersistentProperty getIdentity(Class<T> javaClass, MappingContext context);
/**
* Obtains the default manner in which identifiers are mapped. In GORM
* this is just using a property called 'id', but in other frameworks this
* may differ. For example JPA expects an annotated @Id property
*
* @param classMapping The ClassMapping instance
* @return The default identifier mapping
*/
IdentityMapping getDefaultIdentityMapping(ClassMapping classMapping);
/**
* Returns a set of entities that "own" the given entity. Ownership
* dictates default cascade strategies. So if entity A owns entity B
* then saves, updates and deletes will cascade from A to B
*
* @param javaClass The Java class
* @param context The MappingContext
* @return A Set of owning classes
*/
<T> Set getOwningEntities(Class<T> javaClass, MappingContext context);
}

View File

@@ -0,0 +1,169 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.validation.Validator;
import java.util.Collection;
/**
* <p>This interface defines the overall context including all known
* PersistentEntity instances and methods to obtain instances on demand</p>
* <p/>
* <p>This interface is used internally to establish associations
* between entities and also at runtime to obtain entities by name</p>
* <p/>
* <p>The generic type parameters T & R are used to specify the
* mapped form of a class (example Table) and property (example Column) respectively.</p>
* <p/>
*
* @author Graeme Rocher
* @since 1.0
*/
public interface MappingContext {
/**
* Obtains a list of PersistentEntity instances
*
* @return A list of PersistentEntity instances
*/
Collection<PersistentEntity> getPersistentEntities();
/**
* Obtains a PersistentEntity by name
*
* @param name The name of the entity
* @return The entity or null
*/
PersistentEntity getPersistentEntity(String name);
/**
* Obtains a child of the given root entity using the given discriminator
*
* @param root The root entity
* @param discriminator The discriminator
* @return The child entity or null if non exists
*/
PersistentEntity getChildEntityByDiscriminator(PersistentEntity root, String discriminator);
/**
* Adds a PersistentEntity instance
*
* @param javaClass The Java class representing the entity
* @return The PersistentEntity instance
*/
PersistentEntity addPersistentEntity(Class javaClass);
/**
* Adds a persistent entity that is not mapped by this MappingContext instance.
* Used for cross store persistence
*
* @param javaClass The Java class
* @return The persistent entity
*/
PersistentEntity addExternalPersistentEntity(Class javaClass);
/**
* Adds a validator to be used by the entity for validation
*
* @param entity The PersistentEntity
* @param validator The validator
*/
void addEntityValidator(PersistentEntity entity, Validator validator);
/**
* Add a converter used to convert property values to and from the datastore
*
* @param converter The converter to add
*/
void addTypeConverter(Converter converter);
/**
* Obtains the ConversionService instance to use for type conversion
*
* @return The conversion service instance
*/
ConversionService getConversionService();
/**
* Obtains the converter registry
*
* @return The converter registry used for type conversion
*/
ConverterRegistry getConverterRegistry();
/**
* Obtains a validator for the given entity
*
* @param entity The entity
* @return A validator or null if none exists for the given entity
*/
Validator getEntityValidator(PersistentEntity entity);
/**
* Returns the syntax reader used to interpret the entity
* mapping syntax
*
* @return The SyntaxReader
*/
MappingConfigurationStrategy getMappingSyntaxStrategy();
/**
* Obtains the MappingFactory instance
*
* @return The mapping factory instance
*/
MappingFactory getMappingFactory();
/**
* Returns whether the specified class is a persistent entity
*
* @param type The type to check
* @return True if it is
*/
boolean isPersistentEntity(Class<?> type);
/**
* Returns whether the specified value is a persistent entity
*
* @param value The value to check
* @return True if it is
*/
boolean isPersistentEntity(Object value);
/**
* Adds a new mapping context listener instance
*
* @param listener The listener
*/
void addMappingContextListener(Listener listener);
/**
* Implementors can register for events when the mapping context changes
*/
public static interface Listener {
/**
* Fired when a new entity is added
*
* @param entity The entity
*/
void persistentEntityAdded(PersistentEntity entity);
}
}

View File

@@ -0,0 +1,278 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model;
import org.springframework.data.mapping.model.types.*;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.*;
/**
* <p>An abstract factory for creating persistent property instances.</p>
* <p/>
* <p>Subclasses should implement the createMappedForm method in order to
* provide a mechanisms for representing the property in a form appropriate
* for mapping to the underlying datastore. Example:</p>
* <p/>
* <pre>
* <code>
* class RelationalPropertyFactory<Column> extends PropertyFactory {
* public Column createMappedForm(PersistentProperty mpp) {
* return new Column(mpp)
* }
* }
* </code>
* </pre>
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class MappingFactory<R, T> {
public static final Set<String> SIMPLE_TYPES;
static {
Set<String> basics = new HashSet<String>();
basics.add(boolean.class.getName());
basics.add(long.class.getName());
basics.add(short.class.getName());
basics.add(int.class.getName());
basics.add(byte.class.getName());
basics.add(float.class.getName());
basics.add(double.class.getName());
basics.add(char.class.getName());
basics.add(Boolean.class.getName());
basics.add(Long.class.getName());
basics.add(Short.class.getName());
basics.add(Integer.class.getName());
basics.add(Byte.class.getName());
basics.add(Float.class.getName());
basics.add(Double.class.getName());
basics.add(Character.class.getName());
basics.add(String.class.getName());
basics.add(java.util.Date.class.getName());
basics.add(Time.class.getName());
basics.add(Timestamp.class.getName());
basics.add(java.sql.Date.class.getName());
basics.add(BigDecimal.class.getName());
basics.add(BigInteger.class.getName());
basics.add(Locale.class.getName());
basics.add(Calendar.class.getName());
basics.add(GregorianCalendar.class.getName());
basics.add(java.util.Currency.class.getName());
basics.add(TimeZone.class.getName());
basics.add(Object.class.getName());
basics.add(Class.class.getName());
basics.add(byte[].class.getName());
basics.add(Byte[].class.getName());
basics.add(char[].class.getName());
basics.add(Character[].class.getName());
basics.add(Blob.class.getName());
basics.add(Clob.class.getName());
basics.add(Serializable.class.getName());
basics.add(URI.class.getName());
basics.add(URL.class.getName());
SIMPLE_TYPES = Collections.unmodifiableSet(basics);
}
public MappingFactory() {
super();
}
public static boolean isSimpleType(Class propType) {
if (propType == null) return false;
if (propType.isArray()) {
return isSimpleType(propType.getComponentType());
}
final String typeName = propType.getName();
return isSimpleType(typeName);
}
public static boolean isSimpleType(final String typeName) {
return SIMPLE_TYPES.contains(typeName);
}
/**
* Creates the mapped form of a persistent entity
*
* @param entity The entity
* @return The mapped form
*/
public abstract R createMappedForm(PersistentEntity entity);
/**
* Creates the mapped form of a PersistentProperty instance
*
* @param mpp The PersistentProperty instance
* @return The mapped form
*/
public abstract T createMappedForm(PersistentProperty mpp);
/**
* Creates an identifier property
*
* @param owner The owner
* @param context The context
* @param pd The PropertyDescriptor
* @return An Identity instance
*/
public Identity<T> createIdentity(PersistentEntity owner, MappingContext context, PropertyDescriptor pd) {
return new Identity<T>(owner, context, pd) {
public PropertyMapping<T> getMapping() {
return createPropertyMapping(this, owner);
}
};
}
/**
* Creates a simple property type used for mapping basic types such as String, long, integer etc.
*
* @param owner The owner
* @param context The MappingContext
* @param pd The PropertyDescriptor
* @return A Simple property type
*/
public Simple<T> createSimple(PersistentEntity owner, MappingContext context, PropertyDescriptor pd) {
return new Simple<T>(owner, context, pd) {
public PropertyMapping<T> getMapping() {
return createPropertyMapping(this, owner);
}
};
}
protected PropertyMapping<T> createPropertyMapping(final PersistentProperty<T> property, final PersistentEntity owner) {
return new PropertyMapping<T>() {
public ClassMapping getClassMapping() {
return owner.getMapping();
}
public T getMappedForm() {
return createMappedForm(property);
}
};
}
/**
* Creates a one-to-one association type used for mapping a one-to-one association between entities
*
* @param entity The entity
* @param context The context
* @param property The property
* @return The ToOne instance
*/
public ToOne createOneToOne(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
return new OneToOne<T>(entity, context, property) {
public PropertyMapping getMapping() {
return createPropertyMapping(this, owner);
}
};
}
/**
* Creates a many-to-one association type used for a mapping a many-to-one association between entities
*
* @param entity The entity
* @param context The context
* @param property The property
* @return The ToOne instance
*/
public ToOne createManyToOne(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
return new ManyToOne<T>(entity, context, property) {
public PropertyMapping getMapping() {
return createPropertyMapping(this, owner);
}
};
}
/**
* Creates a {@link OneToMany} type used to model a one-to-many association between entities
*
* @param entity The entity
* @param context The context
* @param property The property
* @return The {@link OneToMany} instance
*/
public OneToMany createOneToMany(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
return new OneToMany<T>(entity, context, property) {
public PropertyMapping getMapping() {
return createPropertyMapping(this, owner);
}
};
}
/**
* Creates a {@link ManyToMany} type used to model a many-to-many association between entities
*
* @param entity The entity
* @param context The context
* @param property The property
* @return The {@link ManyToMany} instance
*/
public ManyToMany createManyToMany(PersistentEntity entity, MappingContext context, PropertyDescriptor property) {
return new ManyToMany<T>(entity, context, property) {
public PropertyMapping getMapping() {
return createPropertyMapping(this, owner);
}
};
}
/**
* Creates an {@link Embedded} type used to model an embedded association (composition)
*
* @param entity The entity
* @param context The context
* @param property The property
* @return The {@link Embedded} instance
*/
public Embedded createEmbedded(PersistentEntity entity,
MappingContext context, PropertyDescriptor property) {
return new Embedded<T>(entity, context, property) {
public PropertyMapping getMapping() {
return createPropertyMapping(this, owner);
}
};
}
/**
* Creates a {@link Basic} collection type
*
* @param entity The entity
* @param context The context
* @param property The property
* @return The Basic collection type
*/
public Basic createBasicCollection(PersistentEntity entity,
MappingContext context, PropertyDescriptor property) {
return new Basic(entity, context, property) {
public PropertyMapping getMapping() {
return createPropertyMapping(this, owner);
}
};
}
}

View File

@@ -0,0 +1,167 @@
package org.springframework.data.mapping.model;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.mapping.model.types.Association;
import java.util.List;
/**
* Represents a persistent entity
*
* @author Graeme Rocher
* @since 1.0
*/
public interface PersistentEntity<T> extends InitializingBean {
/**
* The entity name including any package prefix
*
* @return The entity name
*/
String getName();
/**
* Whether this PersistentEntity is mapped using a different store. Used for cross store persistence
*
* @return True if this entity is externally mapped
*/
boolean isExternal();
/**
* Whether this PersistentEntity is mapped using a different store. Used for cross store persistence
*
* @return True if this entity is externally mapped
*/
void setExternal(boolean external);
/**
* Returns the identity of the instance
*
* @return The identity
*/
PersistentProperty getIdentity();
/**
* A list of properties to be persisted
*
* @return A list of PersistentProperty instances
*/
List<PersistentProperty> getPersistentProperties();
/**
* A list of the associations for this entity. This is typically
* a subset of the list returned by {@link #getPersistentProperties()}
*
* @return A list of associations
*/
List<Association> getAssociations();
/**
* Obtains a PersistentProperty instance by name
*
* @param name The name of the property
* @return The PersistentProperty or null if it doesn't exist
*/
PersistentProperty getPropertyByName(String name);
/**
* @return The underlying Java class for this entity
*/
Class<T> getJavaClass();
/**
* Tests whether the given instance is an instance of this persistent entity
*
* @param obj The object
* @return True if it is
*/
boolean isInstance(Object obj);
/**
* Defines the mapping between this persistent entity
* and an external form
*
* @return The ClassMapping instance
*/
<M> ClassMapping<M> getMapping();
/**
* Constructs a new instance
*
* @return The new instnace
*/
T newInstance();
/**
* A list of property names that a persistent
*
* @return A List of strings
*/
List<String> getPersistentPropertyNames();
/**
* @return Returns the name of the class decapitalized form
*/
String getDecapitalizedName();
/**
* Returns whether the specified entity asserts ownership over this
* entity
*
* @param owner The owning entity
* @return True if it does own this entity
*/
boolean isOwningEntity(PersistentEntity<?> owner);
/**
* Returns the parent entity of this entity
*
* @return The ParentEntity instance
*/
PersistentEntity<?> getParentEntity();
/**
* Obtains the root entity of an inheritance hierarchy
*
* @return The root entity
*/
PersistentEntity<?> getRootEntity();
/**
* Whether this entity is a root entity
*
* @return True if it is a root entity
*/
boolean isRoot();
/**
* The discriminator used when persisting subclasses of an inheritance hierarchy
*
* @return The discriminator
*/
String getDiscriminator();
/**
* Obtains the MappingContext where this PersistentEntity is defined
*
* @return The MappingContext instance
*/
MappingContext getMappingContext();
/**
* Checks whether an entity has a bean property of the given name and type
*
* @param name The name
* @param type The type
* @return True if it does
*/
boolean hasProperty(String name, Class<?> type);
/**
* True if the given property is the identifier
*
* @param propertyName the property name
* @return True if it is the identifier
*/
boolean isIdentityName(String propertyName);
}

View File

@@ -0,0 +1,51 @@
package org.springframework.data.mapping.model;
/**
* @author Graeme Rocher
* @since 1.0
*/
public interface PersistentProperty<T> {
/**
* The name of the property
*
* @return The property name
*/
String getName();
/**
* The name with the first letter in upper case as per Java bean conventions
*
* @return The capitilized name
*/
String getCapitilizedName();
/**
* The type of the property
*
* @return The property type
*/
Class<T> getType();
/**
* Specifies the mapping between this property and an external form
* such as a column, key/value pair etc.
*
* @return The PropertyMapping instance
*/
PropertyMapping<T> getMapping();
/**
* Obtains the owner of this persistent property
*
* @return The owner
*/
PersistentEntity<?> getOwner();
/**
* Whether the property can be set to null
*
* @return True if it can
*/
boolean isNullable();
}

View File

@@ -0,0 +1,25 @@
package org.springframework.data.mapping.model;
/**
* A marker interface for a property mapping which specifies
* what or where a particular property is mapped to
*
* @author Graeme Rocher
* @since 1.0
*/
public interface PropertyMapping<T> {
/**
* Retrieves the ClassMapping instance of the owning class
*
* @return The ClassMapping instance
*/
ClassMapping getClassMapping();
/**
* Returns the mapped form of the property such as a Column, a Key/Value pair, attribute etc.
* @return The mapped representation
*/
T getMappedForm();
}

View File

@@ -0,0 +1,146 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.*;
import javax.persistence.CascadeType;
import javax.persistence.FetchType;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
/**
* Models an association between one class and another
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class Association<T> extends AbstractPersistentProperty {
public static final List<CascadeType> DEFAULT_OWNER_CASCADE = new ArrayList<CascadeType>() {{
add(CascadeType.ALL);
}};
public static final List<CascadeType> DEFAULT_CHILD_CASCADE = new ArrayList<CascadeType>() {{
add(CascadeType.PERSIST);
}};
private PersistentEntity associatedEntity;
private String referencedPropertyName;
private boolean owningSide;
private List<CascadeType> cascadeOperations = new ArrayList<CascadeType>();
private FetchType fetchStrategy = FetchType.EAGER;
public Association(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public Association(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
public FetchType getFetchStrategy() {
return fetchStrategy;
}
public void setFetchStrategy(FetchType fetchStrategy) {
this.fetchStrategy = fetchStrategy;
}
public boolean isBidirectional() {
return getInverseSide() != null;
}
public Association getInverseSide() {
final PersistentProperty associatedProperty = associatedEntity.getPropertyByName(referencedPropertyName);
if(associatedProperty == null) return null;
if(associatedProperty instanceof Association) {
return (Association) associatedProperty;
}
else {
throw new IllegalMappingException("The inverse side ["+associatedEntity.getName()+"." + associatedProperty.getName() +"] of the association ["+getOwner().getName()+"." + this.getName() +"] is not valid. Associations can only map to other entities and collection types.");
}
}
/**
* Returns true if the this association cascade for the given cascade operation
*
* @param cascadeOperation The cascadeOperation
* @return True if it does
*/
public boolean doesCascade(CascadeType cascadeOperation) {
List<CascadeType> cascades = getCascadeOperations();
return cascadeOperation != null && (cascades.contains(CascadeType.ALL) || cascades.contains(cascadeOperation));
}
protected List<CascadeType> getCascadeOperations() {
List<CascadeType> cascades;
if(cascadeOperations.isEmpty()) {
if(isOwningSide()) cascades = DEFAULT_OWNER_CASCADE;
else {
cascades = DEFAULT_CHILD_CASCADE;
}
}
else {
cascades = this.cascadeOperations;
}
return cascades;
}
/**
* Returns whether this side owns the relationship. This controls
* the default cascading behavior if none is specified
*
* @return True if this property is the owning side
*/
public boolean isOwningSide() {
return owningSide;
}
public void setOwningSide(boolean owningSide) {
this.owningSide = owningSide;
}
public void setAssociatedEntity(PersistentEntity associatedEntity) {
this.associatedEntity = associatedEntity;
}
public PersistentEntity getAssociatedEntity() {
return associatedEntity;
}
public void setReferencedPropertyName(String referencedPropertyName) {
this.referencedPropertyName = referencedPropertyName;
}
public String getReferencedPropertyName() {
return referencedPropertyName;
}
@Override
public String toString() {
return getOwner().getName() + "->" + getName();
}
public boolean isList() {
return List.class.isAssignableFrom(getType());
}
public boolean isCircular() {
return isBidirectional() && getAssociatedEntity().equals(getOwner());
}
}

View File

@@ -0,0 +1,65 @@
/* Copyright (C) 2011 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import java.beans.PropertyDescriptor;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import org.springframework.data.mapping.model.PropertyMapping;
/**
* Models a basic collection type such as a list of Strings
*
* @author Graeme Rocher
* @since 1.0
*
*/
public abstract class Basic extends Association {
public Basic(PersistentEntity owner, MappingContext context,
PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public Basic(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
@Override
public Association getInverseSide() {
return null; // basic collection types have no inverse side
}
@Override
public boolean isOwningSide() {
return true;
}
@Override
public void setOwningSide(boolean owningSide) {
// noop
}
@Override
public PersistentEntity getAssociatedEntity() {
return null; // basic collection types have no associated entity
}
}

View File

@@ -0,0 +1,133 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* A registrar that registers basic type converters
*
* @author Graeme Rocher
* @since 1.0
*/
public class BasicTypeConverterRegistrar {
public void register(ConverterRegistry registry) {
registry.addConverter(new Converter<Date, String>() {
public String convert(Date date) {
return String.valueOf(date.getTime());
}
});
registry.addConverter(new Converter<Date, Calendar>() {
public Calendar convert(Date date) {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(date);
return calendar;
}
});
registry.addConverter(new Converter<Integer, Long>() {
public Long convert(Integer integer) {
return integer.longValue();
}
});
registry.addConverter(new Converter<Integer, Double>() {
public Double convert(Integer integer) {
return integer.doubleValue();
}
});
registry.addConverter(new Converter<CharSequence, Date>() {
public Date convert(CharSequence s) {
try {
final Long time = Long.valueOf(s.toString());
return new Date(time);
} catch (NumberFormatException e) {
// ignore
}
return null;
}
});
registry.addConverter(new Converter<CharSequence, Double>() {
public Double convert(CharSequence s) {
try {
return Double.valueOf(s.toString());
} catch (NumberFormatException e) {
return (double) 0;
}
}
});
registry.addConverter(new Converter<CharSequence, Integer>() {
public Integer convert(CharSequence s) {
try {
return Integer.valueOf(s.toString());
} catch (NumberFormatException e) {
// ignore
}
return 0;
}
});
registry.addConverter(new Converter<CharSequence, Long>() {
public Long convert(CharSequence s) {
try {
return Long.valueOf(s.toString());
} catch (NumberFormatException e) {
// ignore
}
return 0L;
}
});
registry.addConverter(new Converter<Object, String>() {
public String convert(Object o) {
return o.toString();
}
});
registry.addConverter(new Converter<Calendar, String>() {
public String convert(Calendar calendar) {
return String.valueOf(calendar.getTime().getTime());
}
});
registry.addConverter(new Converter<CharSequence, Calendar>() {
public Calendar convert(CharSequence s) {
try {
Date date = new Date(Long.valueOf(s.toString()));
Calendar c = new GregorianCalendar();
c.setTime(date);
return c;
} catch (NumberFormatException e) {
return null;
}
}
});
}
}

View File

@@ -0,0 +1,43 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* Models an embedded component
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class Embedded<T> extends ToOne<T> {
public Embedded(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public Embedded(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
@Override
public boolean isOwningSide() {
return true; // embedded instances are always owned
}
}

View File

@@ -0,0 +1,36 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.AbstractPersistentProperty;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* Represents the identity of a persistent entity
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class Identity<T> extends AbstractPersistentProperty {
public Identity(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public Identity(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
}

View File

@@ -0,0 +1,36 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* Models a many-to-many association between one class and another
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class ManyToMany<T> extends Association<T> {
public ManyToMany(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public ManyToMany(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
}

View File

@@ -0,0 +1,37 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* Models a many-to-one association
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class ManyToOne<T> extends ToOne<T> {
public ManyToOne(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public ManyToOne(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
}

View File

@@ -0,0 +1,36 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* Models a one-to-many association
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class OneToMany<T> extends Association<T> {
public OneToMany(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public OneToMany(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
}

View File

@@ -0,0 +1,36 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* Models a one-to-one association
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class OneToOne<T> extends ToOne<T> {
public OneToOne(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public OneToOne(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
}

View File

@@ -0,0 +1,37 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.AbstractPersistentProperty;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* Models a simple property type
*
* @author Graeme Rocher
* @since 1.0
*/
public abstract class Simple<T> extends AbstractPersistentProperty {
public Simple(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public Simple(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
}

View File

@@ -0,0 +1,47 @@
/* Copyright (C) 2010 SpringSource
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.model.types;
import org.springframework.data.mapping.model.MappingContext;
import org.springframework.data.mapping.model.PersistentEntity;
import java.beans.PropertyDescriptor;
/**
* @author Graeme Rocher
* @since 1.1
*/
public abstract class ToOne<T> extends Association<T> {
private boolean foreignKeyInChild;
public ToOne(PersistentEntity owner, MappingContext context, PropertyDescriptor descriptor) {
super(owner, context, descriptor);
}
public ToOne(PersistentEntity owner, MappingContext context, String name, Class type) {
super(owner, context, name, type);
}
public void setForeignKeyInChild(boolean foreignKeyInChild) {
this.foreignKeyInChild = foreignKeyInChild;
}
public boolean isForeignKeyInChild() {
return foreignKeyInChild;
}
}

View File

@@ -0,0 +1,372 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.reflect;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.mapping.annotation.PersistenceConstructor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.*;
/**
* Reads the properties of a class in an optimized manner avoiding
* exceptions
*
* @author Graeme Rocher
* @since 1.0
*/
public class ClassPropertyFetcher {
private static final Log log = LogFactory.getLog(ClassPropertyFetcher.class);
private final Class clazz;
final Map<String, PropertyFetcher> staticFetchers = new HashMap<String, PropertyFetcher>();
final Map<String, PropertyFetcher> instanceFetchers = new HashMap<String, PropertyFetcher>();
private final ReferenceInstanceCallback callback;
private PropertyDescriptor[] propertyDescriptors;
private Constructor preferredConstructor;
private Map<String, PropertyDescriptor> propertyDescriptorsByName = new HashMap<String, PropertyDescriptor>();
private Map<String, Field> fieldsByName = new HashMap<String, Field>();
private Map<Class, List<PropertyDescriptor>> typeToPropertyMap = new HashMap<Class, List<PropertyDescriptor>>();
private static Map<Class, ClassPropertyFetcher> cachedClassPropertyFetchers = new WeakHashMap<Class, ClassPropertyFetcher>();
public static ClassPropertyFetcher forClass(final Class c) {
ClassPropertyFetcher cpf = cachedClassPropertyFetchers.get(c);
if (cpf == null) {
cpf = new ClassPropertyFetcher(c);
cachedClassPropertyFetchers.put(c, cpf);
}
return cpf;
}
ClassPropertyFetcher(final Class clazz) {
this.clazz = clazz;
this.callback = new ReferenceInstanceCallback() {
public Object getReferenceInstance() {
try {
return ReflectionUtils.instantiate(clazz);
} catch (InstantiationException e) {
throw new IllegalStateException(e);
}
}
};
init();
}
/**
* @return The Java that this ClassPropertyFetcher was constructor for
*/
public Class getJavaClass() {
return this.clazz;
}
public Object getReference() {
if (callback != null)
return this.callback.getReferenceInstance();
return null;
}
public PropertyDescriptor[] getPropertyDescriptors() {
return propertyDescriptors;
}
public boolean isReadableProperty(String name) {
return staticFetchers.containsKey(name) || instanceFetchers.containsKey(name);
}
private void init() {
List<Class> allClasses = resolveAllClasses(clazz);
for (Class c : allClasses) {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
processField(field);
}
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
processMethod(method);
}
}
try {
this.propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
} catch (IntrospectionException e) {
// ignore
}
if (propertyDescriptors != null) {
for (PropertyDescriptor desc : propertyDescriptors) {
propertyDescriptorsByName.put(desc.getName(), desc);
final Class<?> propertyType = desc.getPropertyType();
List<PropertyDescriptor> pds = typeToPropertyMap.get(propertyType);
if (pds == null) {
pds = new ArrayList<PropertyDescriptor>();
typeToPropertyMap.put(propertyType, pds);
}
pds.add(desc);
Method readMethod = desc.getReadMethod();
if (readMethod != null) {
boolean staticReadMethod = Modifier.isStatic(readMethod.getModifiers());
if (staticReadMethod) {
staticFetchers.put(desc.getName(), new GetterPropertyFetcher(readMethod, staticReadMethod));
} else {
instanceFetchers.put(desc.getName(), new GetterPropertyFetcher(readMethod, staticReadMethod));
}
}
}
}
Constructor[] constructors = clazz.getConstructors();
if (constructors.length == 1 && constructors[0].getGenericParameterTypes().length > 0) {
preferredConstructor = constructors[0];
} else {
for (Constructor<?> c : constructors) {
if (c.isAnnotationPresent(PersistenceConstructor.class)) {
preferredConstructor = c;
}
}
}
}
public <T> Constructor<T> getPreferredConstructor() {
return preferredConstructor;
}
private void processMethod(Method method) {
if (method.isSynthetic())
return;
if (!Modifier.isPublic(method.getModifiers()))
return;
if (Modifier.isStatic(method.getModifiers())
&& method.getReturnType() != Void.class) {
if (method.getParameterTypes().length == 0) {
String name = method.getName();
if (name.indexOf('$') == -1) {
if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3))) {
name = name.substring(3);
} else if (name.length() > 2
&& name.startsWith("is")
&& Character.isUpperCase(name.charAt(2))
&& (method.getReturnType() == Boolean.class || method.getReturnType() == boolean.class)) {
name = name.substring(2);
}
PropertyFetcher fetcher = new GetterPropertyFetcher(method, true);
staticFetchers.put(name, fetcher);
staticFetchers.put(Introspector.decapitalize(name), fetcher);
}
}
}
}
private void processField(Field field) {
if (field.isSynthetic())
return;
final int modifiers = field.getModifiers();
final String name = field.getName();
if (!Modifier.isPublic(modifiers)) {
if (name.indexOf('$') == -1) {
fieldsByName.put(name, field);
}
} else {
if (name.indexOf('$') == -1) {
boolean staticField = Modifier.isStatic(modifiers);
if (staticField) {
staticFetchers.put(name, new FieldReaderFetcher(field, staticField));
} else {
instanceFetchers.put(name, new FieldReaderFetcher(field, staticField));
}
}
}
}
private List<Class> resolveAllClasses(Class c) {
List<Class> list = new ArrayList<Class>();
Class currentClass = c;
while (currentClass != null) {
list.add(currentClass);
currentClass = currentClass.getSuperclass();
}
Collections.reverse(list);
return list;
}
public Object getPropertyValue(String name) {
return getPropertyValue(name, false);
}
public Object getPropertyValue(String name, boolean onlyInstanceProperties) {
PropertyFetcher fetcher = resolveFetcher(name, onlyInstanceProperties);
return getPropertyValueWithFetcher(name, fetcher);
}
private Object getPropertyValueWithFetcher(String name, PropertyFetcher fetcher) {
if (fetcher != null) {
try {
return fetcher.get(callback);
} catch (Exception e) {
log.warn("Error fetching property's " + name + " value from class " + clazz.getName(), e);
}
}
return null;
}
public <T> T getStaticPropertyValue(String name, Class<T> c) {
PropertyFetcher fetcher = staticFetchers.get(name);
if (fetcher != null) {
Object v = getPropertyValueWithFetcher(name, fetcher);
return returnOnlyIfInstanceOf(v, c);
}
return null;
}
public <T> T getPropertyValue(String name, Class<T> c) {
return returnOnlyIfInstanceOf(getPropertyValue(name, false), c);
}
private <T> T returnOnlyIfInstanceOf(Object value, Class<T> type) {
if ((value != null) && (type == Object.class || ReflectionUtils.isAssignableFrom(type, value.getClass()))) {
return (T) value;
} else {
return null;
}
}
private PropertyFetcher resolveFetcher(String name,
boolean onlyInstanceProperties) {
PropertyFetcher fetcher = null;
if (!onlyInstanceProperties) {
fetcher = staticFetchers.get(name);
}
if (fetcher == null) {
fetcher = instanceFetchers.get(name);
}
return fetcher;
}
public Class getPropertyType(String name) {
return getPropertyType(name, false);
}
public Class getPropertyType(String name, boolean onlyInstanceProperties) {
PropertyFetcher fetcher = resolveFetcher(name, onlyInstanceProperties);
if (fetcher != null) {
return fetcher.getPropertyType(name);
}
return null;
}
public PropertyDescriptor getPropertyDescriptor(String name) {
return propertyDescriptorsByName.get(name);
}
public List<PropertyDescriptor> getPropertiesOfType(Class javaClass) {
final List<PropertyDescriptor> propertyDescriptorList = typeToPropertyMap.get(javaClass);
if (propertyDescriptorList == null) return Collections.emptyList();
return propertyDescriptorList;
}
public List<PropertyDescriptor> getPropertiesAssignableToType(Class assignableType) {
List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
for (Class type : typeToPropertyMap.keySet()) {
if (assignableType.isAssignableFrom(type)) {
properties.addAll(typeToPropertyMap.get(type));
}
}
return properties;
}
public static interface ReferenceInstanceCallback {
public Object getReferenceInstance();
}
static interface PropertyFetcher {
public Object get(ReferenceInstanceCallback callback)
throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException;
public Class getPropertyType(String name);
}
static class GetterPropertyFetcher implements PropertyFetcher {
private final Method readMethod;
private final boolean staticMethod;
GetterPropertyFetcher(Method readMethod, boolean staticMethod) {
this.readMethod = readMethod;
this.staticMethod = staticMethod;
ReflectionUtils.makeAccessible(readMethod);
}
public Object get(ReferenceInstanceCallback callback)
throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
if (staticMethod) {
return readMethod.invoke(null, (Object[]) null);
} else {
if (callback != null) {
return readMethod.invoke(callback.getReferenceInstance(),
(Object[]) null);
} else {
return null;
}
}
}
public Class getPropertyType(String name) {
return readMethod.getReturnType();
}
}
static class FieldReaderFetcher implements PropertyFetcher {
private final Field field;
private final boolean staticField;
public FieldReaderFetcher(Field field, boolean staticField) {
this.field = field;
this.staticField = staticField;
ReflectionUtils.makeAccessible(field);
}
public Object get(ReferenceInstanceCallback callback)
throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
if (staticField) {
return field.get(null);
} else {
if (callback != null) {
return field.get(callback.getReferenceInstance());
} else {
return null;
}
}
}
public Class getPropertyType(String name) {
return field.getType();
}
}
public Field getDeclaredField(String name) {
return fieldsByName.get(name);
}
}

View File

@@ -0,0 +1,209 @@
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mapping.reflect;
import org.springframework.beans.BeanUtils;
import java.beans.PropertyDescriptor;
import java.lang.Boolean;
import java.lang.Byte;
import java.lang.Character;
import java.lang.Class;
import java.lang.Double;
import java.lang.Exception;
import java.lang.Float;
import java.lang.IllegalAccessException;
import java.lang.InstantiationException;
import java.lang.Integer;
import java.lang.Long;
import java.lang.NoSuchMethodException;
import java.lang.NullPointerException;
import java.lang.Object;
import java.lang.Short;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Provides methods to help with reflective operations
*
* @author Graeme Rocher
* @since 1.0
*/
public class ReflectionUtils {
public static final Map<Class<?>, Class<?>> PRIMITIVE_TYPE_COMPATIBLE_CLASSES = new HashMap<Class<?>, Class<?>>();
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
/**
* Just add two entries to the class compatibility map
*
* @param left
* @param right
*/
private static void registerPrimitiveClassPair(Class<?> left, Class<?> right) {
PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(left, right);
PRIMITIVE_TYPE_COMPATIBLE_CLASSES.put(right, left);
}
static {
registerPrimitiveClassPair(Boolean.class, boolean.class);
registerPrimitiveClassPair(Integer.class, int.class);
registerPrimitiveClassPair(Short.class, short.class);
registerPrimitiveClassPair(Byte.class, byte.class);
registerPrimitiveClassPair(Character.class, char.class);
registerPrimitiveClassPair(Long.class, long.class);
registerPrimitiveClassPair(Float.class, float.class);
registerPrimitiveClassPair(Double.class, double.class);
}
/**
* Make the given field accessible, explicitly setting it accessible if necessary.
* The <code>setAccessible(true)</code> method is only called when actually necessary,
* to avoid unnecessary conflicts with a JVM SecurityManager (if active).
* <p/>
* Based on the same method in Spring core.
*
* @param field the field to make accessible
* @see java.lang.reflect.Field#setAccessible
*/
public static void makeAccessible(Field field) {
if (!Modifier.isPublic(field.getModifiers()) ||
!Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
field.setAccessible(true);
}
}
/**
* Make the given method accessible, explicitly setting it accessible if necessary.
* The <code>setAccessible(true)</code> method is only called when actually necessary,
* to avoid unnecessary conflicts with a JVM SecurityManager (if active).
* <p/>
* Based on the same method in Spring core.
*
* @param method the method to make accessible
* @see java.lang.reflect.Method#setAccessible
*/
public static void makeAccessible(Method method) {
if (!Modifier.isPublic(method.getModifiers()) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true);
}
}
/**
* <p>Tests whether or not the left hand type is compatible with the right hand type in Groovy
* terms, i.e. can the left type be assigned a value of the right hand type in Groovy.</p>
* <p>This handles Java primitive type equivalence and uses isAssignableFrom for all other types,
* with a bit of magic for native types and polymorphism i.e. Number assigned an int.
* If either parameter is null an exception is thrown</p>
*
* @param leftType The type of the left hand part of a notional assignment
* @param rightType The type of the right hand part of a notional assignment
* @return True if values of the right hand type can be assigned in Groovy to variables of the left hand type.
*/
public static boolean isAssignableFrom(final Class<?> leftType, final Class<?> rightType) {
if (leftType == null) {
throw new NullPointerException("Left type is null!");
}
if (rightType == null) {
throw new NullPointerException("Right type is null!");
}
if (leftType == Object.class) {
return true;
}
if (leftType == rightType) {
return true;
}
// check for primitive type equivalence
Class<?> r = PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(leftType);
boolean result = r == rightType;
if (!result) {
// If no primitive <-> wrapper match, it may still be assignable
// from polymorphic primitives i.e. Number -> int (AKA Integer)
if (rightType.isPrimitive()) {
// see if incompatible
r = PRIMITIVE_TYPE_COMPATIBLE_CLASSES.get(rightType);
if (r != null) {
result = leftType.isAssignableFrom(r);
}
} else {
// Otherwise it may just be assignable using normal Java polymorphism
result = leftType.isAssignableFrom(rightType);
}
}
return result;
}
/**
* Instantiates an object catching any relevant exceptions and rethrowing as a runtime exception
*
* @param clazz The class
* @return The instantiated object or null if the class parameter was null
*/
public static Object instantiate(Class clazz) throws InstantiationException {
if (clazz == null) return null;
try {
return clazz.getConstructor(EMPTY_CLASS_ARRAY).newInstance();
} catch (IllegalAccessException e) {
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
} catch (InvocationTargetException e) {
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
} catch (NoSuchMethodException e) {
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
} catch (java.lang.InstantiationException e) {
throw new InstantiationException(e.getClass().getName() + " error creating instance of class [" + e.getMessage() + "]: " + e.getMessage());
}
}
/**
* Retrieves all the properties of the given class for the given type
*
* @param clazz The class to retrieve the properties from
* @param propertyType The type of the properties you wish to retrieve
* @return An array of PropertyDescriptor instances
*/
public static PropertyDescriptor[] getPropertiesOfType(Class<?> clazz, Class<?> propertyType) {
if (clazz == null || propertyType == null) {
return new PropertyDescriptor[0];
}
Set<PropertyDescriptor> properties = new HashSet<PropertyDescriptor>();
try {
for (PropertyDescriptor descriptor : BeanUtils.getPropertyDescriptors(clazz)) {
Class<?> currentPropertyType = descriptor.getPropertyType();
if (isTypeInstanceOfPropertyType(propertyType, currentPropertyType)) {
properties.add(descriptor);
}
}
} catch (Exception e) {
// if there are any errors in instantiating just return null for the moment
return new PropertyDescriptor[0];
}
return properties.toArray(new PropertyDescriptor[properties.size()]);
}
private static boolean isTypeInstanceOfPropertyType(Class<?> type, Class<?> propertyType) {
return propertyType.isAssignableFrom(type) && !propertyType.equals(Object.class);
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.dao.DataAccessException;
import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject; import com.mongodb.DBObject;
import org.springframework.data.document.mongodb.convert.MongoConverter;
/** /**
* Abstract base class for unit tests to specify behaviour we expect from {@link MongoOperations}. Subclasses return * Abstract base class for unit tests to specify behaviour we expect from {@link MongoOperations}. Subclasses return

View File

@@ -39,6 +39,7 @@ import org.springframework.data.document.mongodb.query.Index.Duplicates;
import org.springframework.data.document.mongodb.query.Order; import org.springframework.data.document.mongodb.query.Order;
import org.springframework.data.document.mongodb.query.Query; import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.document.mongodb.query.Update; import org.springframework.data.document.mongodb.query.Update;
import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

View File

@@ -24,6 +24,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.convert.SimpleMongoConverter;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import com.mongodb.DB; import com.mongodb.DB;

View File

@@ -29,6 +29,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.document.mongodb.SomeEnumTest.NumberEnum; import org.springframework.data.document.mongodb.SomeEnumTest.NumberEnum;
import org.springframework.data.document.mongodb.SomeEnumTest.StringEnum; import org.springframework.data.document.mongodb.SomeEnumTest.StringEnum;
import org.springframework.data.document.mongodb.convert.SimpleMongoConverter;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObject;

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import org.springframework.data.mapping.annotation.Id;
import org.springframework.data.mapping.annotation.Persistent;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Persistent
public class Account {
@Id
private String id;
private Float balance;
public Float getBalance() {
return balance;
}
public void setBalance(Float balance) {
this.balance = balance;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import java.io.Serializable;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
public class Address implements Serializable {
private String[] lines;
private String city;
private String provinceOrState;
private Integer postalCode;
private String country;
public String[] getLines() {
return lines;
}
public void setLines(String[] lines) {
this.lines = lines;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getProvinceOrState() {
return provinceOrState;
}
public void setProvinceOrState(String provinceOrState) {
this.provinceOrState = provinceOrState;
}
public Integer getPostalCode() {
return postalCode;
}
public void setPostalCode(Integer postalCode) {
this.postalCode = postalCode;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.query.Criteria;
import org.springframework.data.document.mongodb.query.Index;
import org.springframework.data.document.mongodb.query.IndexDefinition;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.endsWith;
import static org.junit.Assert.assertThat;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:mapping.xml")
public class MappingTests {
@Autowired
MongoTemplate template;
@Autowired
MongoMappingContext mappingContext;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void setUp() {
template.dropCollection(template.getDefaultCollectionName());
mappingContext.addPersistentEntity(Person.class);
}
@Test
public void testWrite() {
Person p = new Person(123456789, "John", "Doe", 37);
Address addr = new Address();
addr.setLines(new String[]{"1234 W. 1st Street", "Apt. 12"});
addr.setCity("Anytown");
addr.setPostalCode(12345);
addr.setCountry("USA");
p.setAddress(addr);
Account acct = new Account();
acct.setBalance(1000.00f);
List<Account> accounts = new ArrayList<Account>();
accounts.add(acct);
p.setAccounts(accounts);
template.insert(p);
}
@Test
public void testRead() {
MongoConverter converter = template.getConverter();
List<Person> result = template.find(new Query(Criteria.where("ssn").is(123456789)), Person.class);
assertThat(result.size(), is(1));
assertThat(result.get(0).getAddress().getCountry(), is("USA"));
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.document.mongodb.mapping;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.index.CompoundIndex;
import org.springframework.data.document.mongodb.index.CompoundIndexes;
import org.springframework.data.document.mongodb.index.Indexed;
import org.springframework.data.mapping.annotation.*;
import java.math.BigInteger;
import java.util.List;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
*/
@Persistent
@CompoundIndexes({
@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
})
public class Person {
@Id
private String id;
@Indexed(unique = true)
private Integer ssn;
private String firstName;
@Indexed
private String lastName;
private Integer age;
@Transient
private Integer accountTotal;
@OneToMany
private List<Account> accounts;
private Address address;
public Person(Integer ssn, String firstName, String lastName, Integer age) {
this.ssn = ssn;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
@PersistenceConstructor
public Person(Integer ssn, String firstName, String lastName, Integer age, List<Account> accounts, Address address) {
this.ssn = ssn;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.accounts = accounts;
this.address = address;
}
public Integer getSsn() {
return ssn;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getAccountTotal() {
return accountTotal;
}
public void setAccountTotal(Integer accountTotal) {
this.accountTotal = accountTotal;
}
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}

View File

@@ -25,7 +25,7 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.document.mongodb.MongoConverter; import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.Person; import org.springframework.data.document.mongodb.Person;
import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.repository.query.parser.PartTree;

View File

@@ -26,9 +26,9 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.document.mongodb.MongoConverter; import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.MongoTemplate; import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.data.document.mongodb.SimpleMongoConverter; import org.springframework.data.document.mongodb.convert.SimpleMongoConverter;
import org.springframework.data.document.mongodb.query.BasicQuery; import org.springframework.data.document.mongodb.query.BasicQuery;
/** /**

View File

@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="mongo" class="org.springframework.data.document.mongodb.MongoFactoryBean"> <bean id="mongo" class="org.springframework.data.document.mongodb.MongoFactoryBean">
<property name="host" value="localhost" /> <property name="host" value="localhost"/>
<property name="port" value="27017" /> <property name="port" value="27017"/>
</bean> </bean>
<bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate"> <bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
<constructor-arg ref="mongo" /> <constructor-arg ref="mongo"/>
<constructor-arg value="database" /> <constructor-arg value="database"/>
<property name="defaultCollectionName" value="springdata" /> <property name="defaultCollectionName" value="springdata"/>
</bean> </bean>
<bean class="org.springframework.data.document.mongodb.MongoExceptionTranslator" /> <bean class="org.springframework.data.document.mongodb.MongoExceptionTranslator"/>
</beans> </beans>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="mongo" class="org.springframework.data.document.mongodb.MongoFactoryBean">
<property name="host" value="localhost"/>
<property name="port" value="27017"/>
</bean>
<bean id="mongoConverter" class="org.springframework.data.document.mongodb.convert.MappingMongoConverter">
<property name="mappingContext" ref="mappingContext"/>
<property name="autowirePersistentBeans" value="true"/>
</bean>
<bean id="mongoTemplate" class="org.springframework.data.document.mongodb.MongoTemplate">
<constructor-arg ref="mongo"/>
<constructor-arg value="database"/>
<constructor-arg value="person"/>
<constructor-arg ref="mongoConverter"/>
</bean>
<bean id="mappingContext" class="org.springframework.data.document.mongodb.mapping.MongoMappingContext">
<constructor-arg ref="mongoTemplate"/>
</bean>
<bean class="org.springframework.data.document.mongodb.MongoExceptionTranslator"/>
</beans>

View File

@@ -15,11 +15,17 @@ Import-Template:
org.springframework.data.domain.*;version="[1.0.0, 2.0.0)", org.springframework.data.domain.*;version="[1.0.0, 2.0.0)",
org.springframework.data.document.*;version="[1.0.0, 2.0.0)", org.springframework.data.document.*;version="[1.0.0, 2.0.0)",
org.springframework.data.repository.*;version="[1.0.0, 2.0.0)", org.springframework.data.repository.*;version="[1.0.0, 2.0.0)",
org.springframework.expression.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.common.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.spel.standard.*;version="[3.0.0, 4.0.0)",
org.springframework.expression.spel.support.*;version="[3.0.0, 4.0.0)",
org.springframework.validation.*;version="[3.0.0, 4.0.0)",
com.mongodb.*;version="0", com.mongodb.*;version="0",
org.bson.*;version="0", org.bson.*;version="0",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)", org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.slf4j.*;version="[1.5.0,1.6.0)", org.slf4j.*;version="[1.5.0,1.6.0)",
org.w3c.dom.*;version="0" org.w3c.dom.*;version="0",
javax.persistence.*;version="0"