19
votes

I have made an experiment... one common entity for two Spring data's repositories: - JPA - MongoDB

first of all I' using following libraries versions:

spring-data-jpa : 1.7.0.RELEASE spring-data-mongodb : 1.6.0.RELEASE

I have an Entity:

@Entity
@Table(name = "ACCOUNTS")
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ACCOUNT_ID")
    private Long id;

    @Column(name = "ACCOUNT_NUMBER")
    private String number;

    public Account() {
    }

    public Account(String number) {
        this.number = number;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }
}

JPA Repository has following look:

public interface Repository extends CrudRepository<Account, Long> {
    public Account findByNumber(String number);
}

MongoDB repository has following look:

package ua.home.springdata.investigation.repository.mongo;

public interface Repository extends CrudRepository<Account, Long> {
}

So... JPA works :) Nothing special :) But MongoDB test is not passed :( I'm getting an error:

org.springframework.dao.InvalidDataAccessApiUsageException: Cannot autogenerate id of type java.lang.Long for entity of type ua.home.springdata.investigation.entity.Account!
    at org.springframework.data.mongodb.core.MongoTemplate.assertUpdateableIdIfNotSet(MongoTemplate.java:1149)
    at org.springframework.data.mongodb.core.MongoTemplate.doSave(MongoTemplate.java:878)
    at org.springframework.data.mongodb.core.MongoTemplate.save(MongoTemplate.java:833)
    at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:73)
    at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:88)
    at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:442)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:427)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy26.save(Unknown Source)

I think it's a very common case. Why is not Spring data able to generate entity id as Long? It's so weird.

6
When I am adding the annotation @GeneratedValue my IDE is throwing an error then and there. I have used jpa dependency in my 'pom.xml' file, but even that is not working as what I got is that it searches for a MySQL DB and I am using NoSQL (mongoDB). So if you can help me with something please do. Thanks in advance. - Vibhav Chaddha
@Neil Stockton When I am adding the annotation @GeneratedValue my IDE is throwing an error then and there. I have used jpa dependency in my 'pom.xml' file, but even that is not working as what I got is that it searches for a MySQL DB and I am using NoSQL (mongoDB). So if you can help me with something please do. Thanks in advance.. - Vibhav Chaddha

6 Answers

4
votes

By default the id in mongo collection is string. To maintain a long id of obejcts in collection, you can choose a separate field as follows:

  @Id
  @Field("_id")
  @JsonIgnore
  private String id;

  @Field("my_object_id")
  private Long myObjectId;
34
votes

Mongo ObjectIds don't map to a java Long type.

I see this in the documentation, under 7.6.1:

http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo-template.id-handling

An id property or field declared as a String in the Java class will be converted to and stored as an ObjectId if possible using a Spring Converter. Valid conversion rules are delegated to the MongoDB Java driver. If it cannot be converted to an ObjectId, then the value will be stored as a string in the database.

An id property or field declared as BigInteger in the Java class will be converted to and stored as an ObjectId using a Spring Converter.

So change id to a String or a BigInteger and remove the strategy argument.

6
votes

Using @Id as a String works fine.

Make sure that your Repository extends with a String (same type as the @Id):

extends MongoRepository<MyEntity, String>
4
votes

I think the problem is that you are using Entity instead of Document. Mongo dao's should use Document annotation and the repository should extend the MongoRepository interface. This would be an example using what you have. First you'll want to add the mongo dependency to your pom (I assume you are using spring boot parent, so version number will be defined there)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
import org.springframework.data.annotation.Id; 
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "ACCOUNTS")
public class Account {

    @Id
    private String id;

    ....rest of properties
}

import org.springframework.data.mongodb.repository.MongoRepository;
public interface AccountRepository extends MongoRepository<Account, String>  {
    //any extra queries needed
}
2
votes

I tried something like this too, for mongo db I had to use the import org.springframework.data.annotation.Id; version of @Id whereas JPA I used import javax.persistence.Id;

0
votes

My project using Spring Data Rest + mongo

  1. Data type I am not using either type of Long or BigInteger. It is customized Object. Let's say CompanyUID.class. Here it has to be MongoRepository<DataLoadMerchant, CompanyUID> as what @Miguel says Then I changed my getter and setter. Convert String to CompanyUID or CompanyUID to String.

  2. register converter in Mongo

@Configuration
public class MongoConfig extends AbstractMongoConfiguration {
    @Override
    public CustomConversions customConversions() {
        converters.add(new CompanyUIDoObjectIdConverter());
        converters.add(new ObjectIdToCompanyUIDConverter());
        return new CustomConversions(converters);
    }
}
  1. column name. I look at mongo document. it seems I can not have a entityId with @Id and also use entityId as my column name. So I change setter Then in MongoDB it will have 2 column One is _id and the other one is entityId. The two column keep the same value. And we only use entityId as the primary key for CRUD, even though it is not the true primary key

my code

public class ClassA implements Serializable {
    @Id
    public CompanyUID id;
    public CompanyUID entityId;

    public String getId() {
        return this.id.toString();
    }

    public void setId(String id) {
        if (id != null && this.entityId != null) {
            if (!id.equals(this.entityId)) {
                throw new Exception();
            }
        }
        this.id = new CompanyUID(id);
        this.entityId = this.id;
    }

    public String getEntityId() {
        return entityId.toString();
    }

    public void setEntityId(String entityId) {
        if (this.id != null && entityId != null) {
            if (!id.equals(entityId)) {
                throw new Exception();
            }
        }

        this.entityId = new CompanyUID(entityId);
        this.id = this.entityId;
    }
}