Spring-Boot RestController: Passing Id as String not working - mongodb

I connected my Spring-Boot-Application to a MongoDB. The application is nothing serious, just for getting into working with spring and MongoDB.
The problem it, that my id is a String and I get an Internal Server Error, when I pass the id of a database entry, in order to get it byId...
This is my domain class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Document(collection = "songinfo")
public class SongInfo {
#Id
private String id;
private int songId;
private String songName;
private String description;
}
The Controller-Method:
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}", headers = "Accept=application/json", produces =
{MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = songInfoService.getSongInfoById(id);
if (songInfo == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
The SongInfoServiceImpl:*
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElseThrow(NotFoundException::new);
}
This is the SongsInfoRepository:
public interface SongInfoRepository extends MongoRepository<SongInfo, String> {
}
Getting all songinfos from the database is working fine:
But when is pass the id from one of these entries, I get this:
What is wrong here with my implementation?

You're throwing the exception in SongInfoServiceImpl which is not handled in your SongsInfoController Class.
Solution 1: Instead of throwing the exception return null.
SongInfoServiceImpl.java
#Override
public SongInfo getSongInfoById(String id) {
return songInfoRepository.findById(id).orElse(null);
}
Solution 2: Add try catch block
SongsInfoController.java
#RequiredArgsConstructor
#RestController
#RequestMapping("/songsinfo")
public class SongsInfoController {
private final SongInfoService songInfoService;
#GetMapping(value = "/{id}",
headers = "Accept=application/json",
produces = {MediaType.APPLICATION_JSON_VALUE}
)
public ResponseEntity<SongInfo> getSongInfoById(#PathVariable(value = "id") String id) {
SongInfo songInfo = null;
try {
songInfo = songInfoService.getSongInfoById(id);
} catch(Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(songInfo, HttpStatus.OK);
}
}

I think you need to divide two problem.
Check id parameter SongsInfoController
Inside controller check your parameter is valid through log or sysout
Check getSongInfoById method in SongInfoServiceImpl
Simply getSongInfoById(8752); is get error?
I want to add comment but my reputation is under 50.
If you comment above two solution check result, then I will add additional answer.

Related

Error column specified more than once when inserting an entity with EmbeddedId using Micronaut Data

I'm using Micronaut Data JDBC and I'm facing an error. I have this entity:
#MappedEntity(value = "document_metadata")
#AllArgsConstructor
#EqualsAndHashCode
public class DocumentMetadataJDBCEntity implements DocumentMetadata {
#Embeddable
#AllArgsConstructor
public static class MetadataPk {
#MappedProperty(value = "document_uid")
#NotNull
private UUID documentUid;
#MappedProperty(value = "metadata_key")
#NotNull
private String metadataKey;
public UUID getDocumentUid() {
return documentUid;
}
public String getMetadataKey() {
return metadataKey;
}
}
#EmbeddedId
private MetadataPk metadataPk;
#NotNull
private String metadataValue;
public MetadataPk getMetadataPk() {
return metadataPk;
}
#Override
public String getMetadataKey() {
return getMetadataPk().getMetadataKey();
}
#Override
public String getMetadataValue() {
return metadataValue;
}
public UUID getDocumentUid() {
return getMetadataPk().getDocumentUid();
}
}
And when inserting I get this error:
io.micronaut.data.exceptions.DataAccessException: SQL error executing INSERT: Batch entry 0 INSERT INTO "document_metadata" ("metadata_key","metadata_value","document_uid","document_uid","metadata_key") VALUES ('id','1234','c960d8de-99a4-40a6-91bf-b0d4a73910d6'::uuid,'c960d8de-99a4-40a6-91bf-b0d4a73910d6'::uuid,'id') was aborted: ERROR: column "document_uid" specified more than once
The code for saving is the next one:
Set<DocumentMetadataJDBCEntity> metadataSet = metadata.entrySet().stream()
.map(e -> new DocumentMetadataJDBCEntity(new DocumentMetadataJDBCEntity.MetadataPk(
savedDocument.getUid(), e.getKey()), e.getValue())).collect(toSet());
Iterable<DocumentMetadataJDBCEntity> persistedMetadata = documentMetadataJDBCRepository.saveAll(metadataSet);
Any idea?
Add #Transient to your convenience accessor (getter) methods:
#Override
#Transient
public String getMetadataKey() {
return getMetadataPk().getMetadataKey();
}
#Transient
public UUID getDocumentUid() {
return getMetadataPk().getDocumentUid();
}
It "tells" Micronaut not to save the return value into the DB.

MongoRepository Save method does not insert in database

I have created a SpringBoot project with Jhipster. The database I am using is MongoDB.
In the application-dev.yml I have the following configuration:
data:
mongodb:
uri: mongodb://<user>:<pass>#<ip>:<port>
database: gateway
The user, password, ip Address, and port, in my application-dev are real values.
The DatabaseConfiguration.java is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD)
#Import(value = MongoAutoConfiguration.class)
#EnableMongoAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(DateToZonedDateTimeConverter.INSTANCE);
converters.add(ZonedDateTimeToDateConverter.INSTANCE);
return new MongoCustomConversions(converters);
}
#Bean
public Mongobee mongobee(MongoClient mongoClient, MongoTemplate mongoTemplate, MongoProperties mongoProperties) {
log.debug("Configuring Mongobee");
Mongobee mongobee = new Mongobee(mongoClient);
mongobee.setDbName(mongoProperties.getMongoClientDatabase());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}}
The CloudDatabaseConfiguration is:
#Configuration
#EnableMongoRepositories("es.second.cdti.repository")
#Profile(JHipsterConstants.SPRING_PROFILE_CLOUD)
public class CloudDatabaseConfiguration extends AbstractCloudConfig {
private final Logger log = LoggerFactory.getLogger(CloudDatabaseConfiguration.class);
#Bean
public MongoDbFactory mongoFactory() {
return connectionFactory().mongoDbFactory();
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converterList = new ArrayList<>();
converterList.add(DateToZonedDateTimeConverter.INSTANCE);
converterList.add(ZonedDateTimeToDateConverter.INSTANCE);
converterList.add(DurationToLongConverter.INSTANCE);
return new MongoCustomConversions(converterList);
}
#Bean
public Mongobee mongobee(MongoDbFactory mongoDbFactory, MongoTemplate mongoTemplate, Cloud cloud) {
log.debug("Configuring Cloud Mongobee");
List<ServiceInfo> matchingServiceInfos = cloud.getServiceInfos(MongoDbFactory.class);
if (matchingServiceInfos.size() != 1) {
throw new CloudException("No unique service matching MongoDbFactory found. Expected 1, found "
+ matchingServiceInfos.size());
}
MongoServiceInfo info = (MongoServiceInfo) matchingServiceInfos.get(0);
Mongobee mongobee = new Mongobee(info.getUri());
mongobee.setDbName(mongoDbFactory.getDb().getName());
mongobee.setMongoTemplate(mongoTemplate);
// package to scan for migrations
mongobee.setChangeLogsScanPackage("es.second.cdti.config.dbmigrations");
mongobee.setEnabled(true);
return mongobee;
}
}
The cdtiApp.java is:
#SpringBootApplication
#EnableConfigurationProperties({ApplicationProperties.class})
public class CdtiApp implements InitializingBean{
private static final Logger log = LoggerFactory.getLogger(CdtiApp.class);
private final Environment env;
public CdtiApp(Environment env) {
this.env = env;
}
/**
* Initializes cdti.
* <p>
* Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile
* <p>
* You can find more information on how profiles work with JHipster on https://www.jhipster.tech/profiles/.
*/
#PostConstruct
public void initApplication() {
Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
log.error("You have misconfigured your application! It should not run " +
"with both the 'dev' and 'prod' profiles at the same time.");
}
if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
log.error("You have misconfigured your application! It should not " +
"run with both the 'dev' and 'cloud' profiles at the same time.");
}
}
/**
* Main method, used to run the application.
*
* #param args the command line arguments.
*/
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CdtiApp.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
private static void logApplicationStartup(Environment env) {
String protocol = "http";
if (env.getProperty("server.ssl.key-store") != null) {
protocol = "https";
}
String serverPort = env.getProperty("server.port");
String contextPath = env.getProperty("server.servlet.context-path");
if (StringUtils.isBlank(contextPath)) {
contextPath = "/";
}
String hostAddress = "localhost";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.warn("The host name could not be determined, using `localhost` as fallback");
}
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Local: \t\t{}://localhost:{}{}\n\t" +
"External: \t{}://{}:{}{}\n\t" +
"Profile(s): \t{}\n----------------------------------------------------------",
env.getProperty("spring.application.name"),
protocol,
serverPort,
contextPath,
protocol,
hostAddress,
serverPort,
contextPath,
env.getActiveProfiles());
String configServerStatus = env.getProperty("configserver.status");
if (configServerStatus == null) {
configServerStatus = "Not found or not setup for this application";
}
log.info("\n----------------------------------------------------------\n\t" +
"Config Server: \t{}\n----------------------------------------------------------", configServerStatus);
}
#Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
}
}
The Vehicle entity:
#org.springframework.data.mongodb.core.mapping.Document(collection = "vehicle")
public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
#NotNull
#Field("plate")
private String plate;
#NotNull
#Field("registrationDate")
private Instant registrationDate;
#NotNull
#Field("brand")
private String brand;
#NotNull
#Field("model")
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleDTO is:
public class VehicleDTO {
private String id;
private String plate;
private Instant registrationDate;
private String brand;
private String model;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public Instant getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(Instant registrationDate) {
this.registrationDate = registrationDate;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
The VehicleMapper is:
#Mapper(componentModel = "spring")
public interface VehicleMapper{
Vehicle toEntity(VehicleDTO source);
VehicleDTO toDto(Vehicle target);
}
The VehicleResource is:
#RestController
#RequestMapping("/api")
#CrossOrigin(origins = "*", methods = { RequestMethod.GET, RequestMethod.POST })
public class VehicleResource {
private final Logger log = LoggerFactory.getLogger(VehicleResource.class);
#Value("${jhipster.clientApp.name}")
private String applicationName;
#Autowired
private final VehicleService vehicleService;
public VehicleResource(VehicleService vehicleService) {
this.vehicleService = vehicleService;
}
#PostMapping("/vehicle")
#PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
public ResponseEntity<Vehicle> createVehicle(#Valid #RequestBody VehicleDTO vehicleDTO) throws URISyntaxException {
log.debug("REST request to save Vehicle : {}", vehicleDTO);
Vehicle newVehicle = vehicleService.createVehicle(vehicleDTO);
return ResponseEntity.created(new URI("/api/vehicle/" + newVehicle.getPlate()))
.headers(HeaderUtil.createAlert(applicationName, "vehicleManagement.created", newVehicle.getPlate()))
.body(newVehicle);
}
}
The VehicleService interface is:
public interface VehicleService {
Vehicle createVehicle(VehicleDTO vehicleDTO);
}
The VehicleServiceImpl is:
#Service
public class VehicleServiceImpl implements VehicleService{
#Autowired
private final VehicleRepository vehicleRepository;
#Autowired
private final VehicleMapper mapper;
public VehicleServiceImpl(VehicleRepository vehicleRepository, VehicleMapper mapper) {
this.vehicleRepository = vehicleRepository;
this.mapper = mapper;
}
private final Logger log = LoggerFactory.getLogger(VehicleServiceImpl.class);
#Override
public Vehicle createVehicle(VehicleDTO vehicleDTO) {
Vehicle vehicle = vehicleRepository.save(mapper.toEntity(vehicleDTO));
log.debug("Created Information for vehicle: {}", vehicle);
return vehicle;
}
}
The VehicleRepository interface is:
/**
* Spring Data MongoDB repository for the {#link Vehicle} entity.
*/
#Repository
public interface VehicleRepository extends MongoRepository<Vehicle, String> {
}
From the Swagger console I access the Vehicle-Resource:
Swagger console
Click on the button and write in the text box the json with the vehicle data:
enter JSON data
As we can see in the following image, the answer is 201. Initially the vehicle was saved with the identifier "id": "60e740935ed5a10e2c2ed19e".
Send request
I access the database to check that the vehicle has been correctly stored in the vehicle table. To my surprise ... there is no vehicle in the vehicle table:
show database
I can make sure that the data in the database application-dev is OK. I don't have any other databases.
I suspect that transactions with the database are not actually being made. This data is somehow stored in memory because if I do a findAllVehicles from Swagger it does return the vehicle.
I have a eureka server running (jhipster-registry) and two microservices that synchronize with it. The Gateway, which acts as a reverse proxy and the Vehiculos microservice. The Swagger console is the gateway, from where I make the request to insert vehicles. Everything seems to work, but as I say in bbdd does not save anything.

Rest API with Spring Data MongoDB - Repository method not working

I am reading and learning Spring Boot data with MongoDB. I have about 10 records in my database in the following format:
{
"_id" : ObjectId("5910c7fed6df5322243c36cd"),
name: "car"
}
When I open the url:
http://localhost:8090/items
I get an exhaustive list of all items. However, I want to use the methods of MongoRepository such as findById, count etc. When I use them as such:
http://localhost:8090/items/count
http://localhost:8090/items/findById/5910c7fed6df5322243c36cd
http://localhost:8090/items/findById?id=5910c7fed6df5322243c36cd
I get a 404.
My setup is as so:
#SpringBootApplication
public class Application {
public static void main(String[] args) throws IOException {
SpringApplication.run(Application.class, args);
}
}
#Document
public class Item implements Serializable {
private static final long serialVersionUID = -4343106526681673638L;
#Id
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#RepositoryRestResource(collectionResourceRel = "item", path = "items")
public interface ItemRepository<T, ID extends Serializable> extends MongoRepository<Item, String>, ItemRepositoryCustom {
}
What am I doing wrong? Do I need to implement the methods as defined by MongoRepository or will they be automatically implemented? I am lost and have been trying to figure this out for so long. I do not have any methods in my controller, its empty.
You have to declare the findById method in order for it to be exposed.
Item findById(String id);
Item findByName(String name);
Note that you don't need to implement the methods. SpringBoot will analyse the method name and provide the proper implementation
I had same issue,
After removing #Configuration,#ComponentScan everything worked fine.

How to extract path variable from responseentity in Junit testing

Searched but unfortunately I do not get similar questions. I've pasted my involved codes. It uses Spring DATA framework.
Entity EscalationPolicy with ID automatically generated
controller to hand POST request to create an new policy
update JUnit Test
What I'm trying to do in the test is that first create one new EscalationPolicy with the object set by initTest(). Then fetch and update it. However the ID is unknown and I suppose I need to extract it from the return URI. I don't know how to do it after Mockmvc perform and appreciate any help. Thanks!
#Entity
#Table(name = "T_ESCALATIONPOLICY")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class EscalationPolicy implements Serializable {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
#Column(name = "policy_name")
private String policy_name;
...
}
#RestController
#RequestMapping("/api")
public class EscalationPolicyResource {
...
/**
* POST /escalationPolicys -> Create a new escalationPolicy.
*/
#RequestMapping(value = "/escalationPolicys",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<Void> create(#RequestBody EscalationPolicy escalationPolicy) throws URISyntaxException {
log.debug("REST request to save EscalationPolicy : {}", escalationPolicy);
if (escalationPolicy.getId() != null) {
return ResponseEntity.badRequest().header("Failure", "A new escalationPolicy cannot already have an ID").build();
}
escalationPolicyRepository.saveAndFlush(escalationPolicy);
return ResponseEntity.created(new URI("/api/escalationPolicys/" + escalationPolicy.getId())).build();
}
...
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
public class EscalationPolicyResourceTest {
#Before
public void initTest() {
escalationPolicy = new EscalationPolicy();
escalationPolicy.setPolicy_name("Policy Test");
...
}
#Test
#Transactional
public void updatePolicy() throws Exception {
// Create the EscalationPolicy
restEscalationPolicyMockMvc.perform(post("/api/escalationPolicys")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(escalationPolicy)))
.andExpect(status().isCreated());
// Get the created policy
EscalationPolicy e = escalationPolicyRepository.findOne(id);
~~need ID here
}
...
}
Though it may not be the most elegant way to deal with it, I think a way to bypass the problem. I save the id in the header map and in the test code to extract it.
#RestController
#RequestMapping("/api")
public class EscalationPolicyResource {
...
/**
* POST /escalationPolicys -> Create a new escalationPolicy.
*/
#RequestMapping(value = "/escalationPolicys",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<Void> create(#RequestBody EscalationPolicy escalationPolicy) throws URISyntaxException {
log.debug("REST request to save EscalationPolicy : {}", escalationPolicy);
if (escalationPolicy.getId() != null) {
return ResponseEntity.badRequest().header("Failure", "A new escalationPolicy cannot already have an ID").build();
}
escalationPolicyRepository.saveAndFlush(escalationPolicy);
HttpHeaders headers = new HttpHeaders();
headers.set("policyID", escalationPolicy.getId());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
...
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
public class EscalationPolicyResourceTest {
#Before
public void initTest() {
escalationPolicy = new EscalationPolicy();
escalationPolicy.setPolicy_name("Policy Test");
...
}
#Test
#Transactional
public void updatePolicy() throws Exception {
// Create the EscalationPolicy
ResultActions action =
restEscalationPolicyMockMvc.perform(post("/api/escalationPolicys")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(escalationPolicy)));
action.andExpect(status().isCreated());
id = (String)action.andReturn().getResponse().getHeaderValue("policyID");
// Get the created policy
EscalationPolicy e = escalationPolicyRepository.findOne(id);
}
...
}

play2 java form binding - how to set field name to map to object?

Say I have the below test case
I want to be able to bind camel case parameters:
anyData.put("my_id", "bob#gmail.com");
How can I get this test to pass??
public class FormBindingExampleTest {
public static class FormBindingExampleModel {
public String myid;
public String email;
public String getMyid() {
return myid;
}
public void setMyid(String myid) {
this.myid = myid;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
#Test
public void itShouldBindForm(){
Form<FormBindingExampleModel> userForm = form(FormBindingExampleModel.class);
Map<String,String> anyData = new HashMap();
anyData.put("my_id", "bob#gmail.com");
anyData.put("email", "secret");
FormBindingExampleModel user = userForm.bind(anyData).get();
System.out.println(user.myid);
assert(user.myid.equals("bob#gmail.com"));
}
}
Use form's fill() method inorder to populate the form with existing value.
#Test
public void itShouldBindForm(){
Form<FormBindingExampleModel> userForm = form(FormBindingExampleModel.class);
FormBindingExampleModel formModel = new FormBindingExampleModel();
formModel.setMyid("bob#gmail.com");
formModel.setEmail("secret");
userForm.fill(formModel);
FormBindingExampleModel user = userForm.get();
System.out.println(user.getMyid);
assert(user.getMyid.equals("bob#gmail.com"));
}
Documentation available here.