MappingException: Ambiguous field mapping detected

5k views Asked by At

Using Spring boot 1.5.6.RELEASE.

I have the following mongo document base class:

@Document(collection="validation_commercial")
public abstract class Tier {
    @Id
    private String id;
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    private Date created;
    @Field("tran")
    private Tran tran;

    public Tier() {
    }

    public String getId() {
        return id;
    }

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

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Tran getTran() {
        return tran;
    }

    public void setTran(Tran tran) {
        this.tran = tran;
    }
}

which is then extended:

public class Tier1 extends Tier {

    @Field("tier1")
    private Tier1Programs tier1;

    public Tier1() {
        this.tier1 = new Tier1Programs();
    }

    public Tier1Programs getTier1() {
        return tier1;
    }

    public void setTier1(Tier1Programs tier1) {
        this.tier1 = tier1;
    }
}

which in turn is extended:

public class Tier2 extends Tier1 {

    @Field("tier2")
    private Tier2Programs tier2;

    public Tier2() {
        this.tier2 = new Tier2Programs();
    }

    public Tier2Programs getTier2() {
        return tier2;
    }

    public void setTier2(Tier2Programs tier2) {
        this.tier2 = tier2;
    }
}

There is a Tier1 Supervisor (Spring Boot Application) that uses the Tier1 class within the MongoRepository interface:

public interface Tier1Repository extends MongoRepository<Tier1,String>{}

for retrieving and saving - no issue.

I then have a Tier2 Supervisor (Spring Boot Application) that uses a Tier1 Repository (for retrieving the Tier1 document and a Tier2 Repository for saving the Tier2 document:

@Repository("tier1Repository")
public interface Tier1Repository extends MongoRepository<Tier1,String>{}

@Repository("tier2Repository")
public interface Tier2Repository extends MongoRepository<Tier2,String>{}

My service is:

@Service
public class TierService {
    @Qualifier("tier1Repository")
    @Autowired
    private final Tier1Repository tier1Repository;
    @Qualifier("tier2Repository")
    @Autowired
    private final Tier2Repository tier2Repository;

    public TierService(@Qualifier("tier1Repository") Tier1Repository tier1Repository, @Qualifier("tier2Repository") Tier2Repository tier2Repository) {
        this.tier1Repository = tier1Repository;
        this.tier2Repository = tier2Repository;
    }

    public Tier1 findOne(String id) {
        return tier1Repository.findOne(id);
    }

    public void SaveTier(Tier2 tier) {
        tier2Repository.save(tier);
    }

    public Tier1Repository getTier1Repository() {
        return tier1Repository;
    }

    public Tier2Repository getTier2Repository() {
        return tier2Repository;
    }
}

and finally the app:

@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class})
@Configuration
@ComponentScan(basePackages = {"com.k12commercial.tier2supervisor"})
@ImportResource("classpath:application-context.xml")
public class Application implements CommandLineRunner {

    @Autowired
    private IReceiver raBidNetPriceReceiver;

    @Autowired
    private UdyDataSourceFactory udyDSRegistry;

    public static void main(String[] args) throws InterruptedException {
        try {
            SpringApplication.run(Application.class, args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }    

    @Override
    public void run(String... args) throws Exception {
        raBidNetPriceReceiver.processTierMessages();
        exit(0);
    }
}

When I run the Tier2 Supervisor from the command line I get the following error:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tierService' defined in URL [jar:file:/opt/java-commandline/tier2supervisor-1.0.jar!/BOOT-INF/classes!/com/k12commercial/tier2supervisor/service/TierService.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tier2Repository': Invocation of init method failed; nested exception is org.springframework.data.mapping.model.MappingException: Ambiguous field mapping detected! Both private final java.lang.reflect.Type org.springframework.data.util.TypeDiscoverer.type and private final java.lang.Class org.springframework.data.util.ClassTypeInformation.type map to the same field name type! Disambiguate using @Field annotation!

I am not sure if the issue is Tier2 extending Tier1 (did try putting @Document tag above Tier1 and Tier2 with no change). I think I have marked the relevant fields so don't understand the need to disambiguate. I thought the issue was having 2 repositories (Spring Boot not knowing which one to DI) so removed the Tier1Repository - didn't work. Tried better qualifying the repositories but still got the same error. I made Tier1 and Tier2 @Transient and that got rid of the message but also removed the tier1 section in the mongo document - so wrong correction.

Thinking it is an annotation fix but not seeing it...

Please advise - thank you.

1

There are 1 answers

0
PgB On

Sorry for the delay (I got pulled away to work on something else) and thank you to those who responded.

The issue was I had a MongoTemplate in my Tier level programs e.g.Tier2Programs (sub library) which Spring Boot was trying to autowire.

By moving the Mongo (CRUD) requirements to the supervisor level (I also replaced the Repositories with one MongoTemplate to simplify) I removed the ambiguity. (I also removed the Service class).

The code is contained with the RaBidNetReciever class

    @Component
public class RaBidNetPriceReceiver extends BaseReceiver implements IReceiver, ApplicationEventPublisherAware {
    private static final Logger LOGGER = LoggerFactory.getLogger(RaBidNetPriceReceiver.class);
    private final RabbitTemplate raBidNetPriceRabbitTemplate;

    public RaBidNetPriceReceiver(MongoTemplate mongoTemplate, RabbitTemplate raBidNetPriceRabbitTemplate) {
    super(mongoTemplate);
    this.raBidNetPriceRabbitTemplate = raBidNetPriceRabbitTemplate;
    }

    @Transactional
    public void processTierMessages() {
    try {
        while (true) {
            gson = getGsonBuilder().create();
            byte[] body = (byte[]) raBidNetPriceRabbitTemplate.receiveAndConvert();
            if (body == null) {
                setFinished(true);
                break;
            }
            tier1Message = gson.fromJson(new String(body), Tier1Message.class);
            // document a 'Tier1' type so retrieve Tier1 first...
            Tier1 tier1 = mongoTemplate.findById(tier1Message.getId(), Tier1.class);

            Tier2Message tier2Message = new Tier2Message(tier1Message.getTran(), tier1Message.getId());
            Tier2Process tierProcess = getTierProcess(tier2Message.getTran().getK12ArchitectureId());

            Tier2 tier2 = new Tier2();
            tier2.setId(tier1.getId());
            tier2.setTier1Programs(tier1.getTier1Programs());
            tier2.setCreated(tier1.getCreated());
            tier2.setTran(tier1.getTran());

            tierProcess.setTier(tier2);
            tier2 = tier2.getTier2Programs().getRaBidNetPriceProgram().process(tierProcess);
            mongoTemplate.save(tier2);

            if (tier2.getTier2Programs().getRaBidNetPriceProgram().isFinished()) {
                // publish event
                publisher.publishEvent(new ProgramEvent(this, "FINISHED", tier2Message));
            }
        }

     } catch (Exception e) {
        LOGGER.error("id: " + tier1Message.getId() + " " + e.getMessage());
     }
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    this.publisher = applicationEventPublisher;
    }
}

Thank you,