23

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.

2
  • 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. Commented Mar 31, 2017 at 9:08
  • @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.. Commented Mar 31, 2017 at 9:09

7 Answers 7

39

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<String, ObjectId>. 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<BigInteger, ObjectId>.

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

Sign up to request clarification or add additional context in comments.

3 Comments

Then i will lose compatibility with Spring JPA implementation :(
@Robert Moskal 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.
Add a new question and tag me
9

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;

Comments

8

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>

Comments

6

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
}

Comments

2

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;

Comments

1

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;
    }
}

Comments

0

The MongoDb or Jpa will not work for crud function if you are taking id as int or long.

Always use string data type for autogenerate variable

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.