How do I check if the value that is inserted into the collection using a MongoTemplate is saved successfully?
MongoTemplate template;
User user=new User();
user.name="Mark";
user.email="mark#mark.com"
template.insert(user);
Insert , save or any method in MongoTemplate does not return true or false.
It will throw an exception here is the flow of methods:
template.insert(object)
|
|
templateinsert(Object objectToSave, String collectionName)
|
|
protected <T> void doInsert(String collectionName, T objectToSave, MongoWriter<T> writer)
|
|
protected Object insertDBObject(final String collectionName, final DBObject dbDoc, final Class<?> entityClass)
|
|
Here is the source code for insertDBObject
protected Object insertDBObject(final String collectionName, final DBObject dbDoc, final Class<?> entityClass) {
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("Inserting DBObject containing fields: {} in collection: {}", dbDoc.keySet(), collectionName);
}
return this.execute(collectionName, new CollectionCallback() {
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
MongoAction mongoAction = new MongoAction(MongoTemplate.this.writeConcern, MongoActionOperation.INSERT, collectionName, entityClass, dbDoc, (DBObject)null);
WriteConcern writeConcernToUse = MongoTemplate.this.prepareWriteConcern(mongoAction);
WriteResult writeResult = writeConcernToUse == null?collection.insert(new DBObject[]{dbDoc}):collection.insert(dbDoc, writeConcernToUse);
MongoTemplate.this.handleAnyWriteResultErrors(writeResult, dbDoc, MongoActionOperation.INSERT);
return dbDoc.get("_id");
}
});
}
which will throw exception when you write fails, They are runtime exception. Runtime excpetions are translated by MongoExceptionTranslator
.So any insert which doesn't throw any exception is successful.
Related
It is necessary to deserialize the result from Mono<ResultSumDto> to JSON, then to sent to the client as JSON.
Controller
#GetMapping("v1/sequence/{startRange}/{endRange}")
Mono<ResultSumDto > getSumFromRange(
#PathVariable BigInteger startRange,
#PathVariable BigInteger endRange) {
ResultSumDto resultSumDto = ...
return Mono.just(resultSumDto);
}
#Configuration
public class JacksonObjectMapperConfiguration {
#Autowired
public void serializeBigInteger(ObjectMapper objectMapper) {
JsonFormat.Value formatValue =
JsonFormat.Value.forShape(JsonFormat.Shape.STRING);
objectMapper
.configOverride(BigInteger.class)
.setFormat(formatValue);
}
}
#Data
#Builder
public class ResultSumDto {
private final BigInteger sumSeq;
private final BigInteger [] seqRange;
private final Boolean isCached;
}
private Mono<ResultSumDto> buildResult(SeqDto dto) {
Mono<BigInteger> sumSeq =
calculateSumRangeValuesFibonacciSequence(dto);
BigInteger bigInteger = null;
try {
bigInteger = sumSeq
.toFuture()
.get();
} catch (InterruptedException | ExecutionException e) {
log.error(e.getLocalizedMessage());
Thread.currentThread().interrupt();
}
BigInteger[] rangeGiven = new BigInteger[]
{dto.getStartRange(), dto.getEndRange()};
return Mono.just(ResultSumSeqDto.builder()
.sumSequence(bigInteger)
.sequenceRange(rangeGiven)
.isCached(false)
.build()
);
}
But I have a mistake:
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize value of type java.math.BigInteger from Object value (token JsonToken.START_OBJECT); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.math.BigInteger from Object value (token JsonToken.START_OBJECT)
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
But after all, when I get values in endpoint, serialization to the BigInteger type goes without problems.
Who has any idea why it doesn't work and how it can be fixed. Share your knowledge on how to deserialize an array BigInteger and a field with the BigInteger type?
That's what worked in my case.
public class DeserializeResultCalculateSumSequence
extends StdDeserializer<ResultCalculateSumSequenceDto> {
public DeserializeResultCalculateSumSequence() {
this(null);
}
protected DeserializeResultCalculateSumSequence(Class<?> vc) {
super(vc);
}
#Override
public ResultCalculateSumSequenceDto deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext)
throws IOException, JacksonException {
JsonNode node = jsonParser
.getCodec()
.readTree(jsonParser);
BigInteger sumSequence = node
.get("sumSequence")
.bigIntegerValue();
ObjectMapper mapper = new ObjectMapper();
String sequenceRangeStr = node.get("sequenceRange").toString();
BigInteger[] sequenceRange = mapper
.readValue(sequenceRangeStr, BigInteger[].class);
boolean isCached = node
.get("isCached")
.asBoolean();
return ResultCalculateSumSequenceDto
.builder()
.sumSequence(sumSequence)
.sequenceRange(sequenceRange)
.isCached(isCached)
.build();
}
}
#Data
#Builder
#JsonDeserialize(using = DeserializeResultCalculateSumSequence.class)
public class ResultCalculateSumSequenceDto {
private final BigInteger sumSequence;
private final BigInteger [] sequenceRange;
private final Boolean isCached;
}
When I run test then it failed at this point ReflectionTestUtils.setField(service, SeRepositoryImpl.class, "isBoolean",true,Boolean.class) complains about Could not find field 'isBoolean' of type not found. Error trace as below.
I am not sure why because my repositoryImpl class has isBoolean variable defined.
java.lang.IllegalArgumentException: Could not find field 'isBoolean' of type [class java.lang.Boolean] on target object [lautomation.repository.impl.SaRepositoryImpl#4a178d1e] or target class [lautomation.repository.impl.SaRepositoryImpl]
at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:175)
test class looks like
#MockBean(name = "seRepository")
PolicyRepository seRepository;
#InjectMocks
private SeRepositoryImpl service;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpdateStatus() throws Exception{
ReflectionTestUtils.setField(service, SeRepositoryImpl.class, "isBoolean",true,Boolean.class);
List<Policy> policies = Arrays.asList(new Policy[] {new Policy() });
service.updateIssuedStatus(Mockito.any(Policy.class));
Mockito.verify(seRepository, Mockito.times(1)).updateIssuedStatus(Mockito.any(Policy.class));
}
}
Respository implementation class SeRepositoryImpl has isBoolean variable defined
#Repository("seRepository")
#Log4j
public class SeRepositoryImpl implements PolicyRepository {
#Value("${change-db}")
private boolean isBoolean;
#Autowired
#Qualifier("jdbcDbName")
private NamedParameterJdbcTemplate jdbcTemplate;
#Override
public void updateIssuedStatus(final Policy policy) {
if(!isBoolean) {
log.warn("isBoolean is set to false - skipping db write.");
return;
}
final HashMap<String, String> params = new HashMap<>();
params.put("issued",
new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
params.put("id", Integer.toString(policy.getId()));
jdbcTemplate.update(updateIssuedStatus, params);
String currDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
log.info("params:"+Integer.toString(policy.getId())+" Date:"+currDate);
final String sql = "call usp_updateDatabase(:policy,:currDate)";
MapSqlParameterSource value = new MapSqlParameterSource();
value.addValue("id",Integer.toString(policy.getId()));
value.addValue("stop_dt",new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
jdbcTemplate.update(sql, value);
}
}
I've set this method to return a response from a Spring Boot rest controller:
public ResponseEntity<Map<String, Object>> get(#PathVariable("id") long id) {
try {
return new ResponseEntity<>(this.ReportDAO.read("dbuser1"), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
And this is the DAO method:
#Autowired
private JdbcTemplate jdbcTemplate;
public Map<String, Object> read(String testParam) {
List<SqlParameter> parameters = Arrays.asList(new SqlParameter(Types.NVARCHAR));
CallableStatementCreator csc = new CallableStatementCreator() {
#Override
public CallableStatement createCallableStatement(Connection con) throws SQLException {
CallableStatement cs = con.prepareCall("{call test (?)}");
cs.setString(1, testParam);
return cs;
}
};
return jdbcTemplate.call(csc, parameters);
}
I'm successfully having a json object as response but in this format:
#result-set-1: [ {…}, {…} ]
while I'm expecting to have:
[ {…}, {…} ]
Why is the resultset inserted into #result-set-1 key? How can I modify this behaviour?
JdbcTemplate#call returns Map<String, Object> You can alter this behaviour by specifically extracting key from map using key #result-set-1.
This is how i have done it:
sql
CREATE TABLE `sample_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`message` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Insert statements:
insert into sample_log (message) values('West Country');
insert into sample_log (message) values('Welcome User');
Stored procedure:
CREATE PROCEDURE `fetch_sample_logs`(
in message_query varchar(30)
)
BEGIN
SELECT * FROM new_db.sample_log where message like message_query;
END
Controller
#RequestMapping("/logs")
#RestController
class SampleLogController {
private final JdbcTemplate jdbcTemplate;
#Autowired
SampleLogController(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#GetMapping("/call")
public Object get() {
final Map<String, Object> call = jdbcTemplate.call(connection -> {
CallableStatement cs = connection.prepareCall("{call fetch_sample_logs (?)}");
cs.setString(1, "%wel%");
return cs;
}, Collections.singletonList(new SqlParameter(Types.VARCHAR)));
return Optional.of(call.getOrDefault("#result-set-1", Collections.emptyList()));
}
}
I am suggesting that you extract all the result-sets and concat them together. You could do as the other answer suggests and just get "#result-set-1" from the Map, but I would suggest at the very least converting the ResultSet to an application-represented object ("Thing" pojo) before returning from the dao method. I think that concatening the result-sets together is probably a more durable solution, unless someone can think of a reason as to why not.
#Autowired
private JdbcTemplate jdbcTemplate;
public List<Thing> read(String testParam) {
List<SqlParameter> parameters = Arrays.asList(new SqlParameter(Types.NVARCHAR));
CallableStatementCreator csc = new CallableStatementCreator() {
#Override
public CallableStatement createCallableStatement(Connection con) throws SQLException {
CallableStatement cs = con.prepareCall("{call test (?)}");
cs.setString(1, testParam);
return cs;
}
};
Map<String, Object> result = jdbcTemplate.call(csc, parameters);
return result.values().stream().map(o -> fromResultSet((ResultSet) o)
.flatMap(List::stream).collect(toList());
}
private List<Thing> fromResultSet(ResultSet resultSet) {
List<Thing> list = new ArrayList<>();
while (resultSet.next()) {
Thing thing = new Thing(resultSet.getString("resultCol1"), resultSet.getString("resultCol2")
list.add(user);
}
}
I modified some code from Resultset To List to actually parse the result set.
please find below my sample code.The Row mapper returns a list. When printed it give me the size in the DB but when i check
(List) employeeDaomap .get("allEmployees") i get the list size as 1 , and entire rows as one item? why what is the wrong in implementation
Also Spring doc says not to use rs.next(), how do we get the list of
values from the DB
public class MyTestDAO extends StoredProcedure {
/** The log. */
static Logger log = Logger.getLogger(MyTestDAO.class);
private static final String SPROC_NAME = "TestSchema.PKG_Test.prc_get_employee_list";
TestRowMapper mapper=new TestRowMapper();
public MyTestDAO(DataSource dataSource){
super(dataSource, SPROC_NAME);
declareParameter(new SqlOutParameter("allEmployees", OracleTypes.CURSOR, mapper));
compile();
}
/**
* Gets the myemplist data from the DB
*
*/
public List<EmployeeDAO> getEmployeeList()
throws Exception {
Map<String,Object> employeeDaomap =new HashMap<String,Object>();
employeeDaomap =execute();
log.info("employeeDaomap after execute ="+employeeDaomap);
log.info("employeeDaomap after execute size ="+employeeDaomap.size()); // expected 1
List<EmployeeDAO> list = (List<EmployeeDAO>) employeeDaomap .get("allEmployees");
log.info("size of the list ="+list.size()); // need to get the size of the list ,
return list;
}
private Map<String, Object> execute() {
return super.execute(new HashMap<String, Object>());
}
}
public class TestRowMapper implements RowMapper<List<EmployeeDAO>> {
static Logger log = Logger.getLogger(TestRowMapper.class);
#Override
public List<EmployeeDAO> mapRow(ResultSet rs, int rowNum)
throws SQLException {
// TODO Auto-generated method stub
rs.setFetchSize(3000);
List<EmployeeDAO> responseItems = new ArrayList<EmployeeDAO>();
EmployeeDAO responseItem = null;
log.info("row num "+rowNum);
while (rs.next()) {
responseItem = new EmployeeDAO();
responseItem.setID(rs.getString("id"));
responseItem.setName(rs.getString("name"));
responseItem.setDesc(rs.getString("desc"));
responseItems.add(responseItem);
}
log.info("TestRowMapper items ="+responseItems);
return responseItems;
}
}
The solution is to use the implements ResultSetExtractor instead of RowMapper and provide implementation for extractData.
public class TestRowMapper implements ResultSetExtractor<List<EmployeeDAO>> {
static Logger log = Logger.getLogger(TestRowMapper.class);
#Override
public List<EMAccountResponse> extractData(ResultSet rs)
throws SQLException, DataAccessException {
rs.setFetchSize(3000);
List<EmployeeDAO> responseItems = new ArrayList<EmployeeDAO>();
EmployeeDAO responseItem = null;
log.info("row num "+rowNum);
while (rs.next()) {
responseItem = new EmployeeDAO();
responseItem.setID(rs.getString("id"));
responseItem.setName(rs.getString("name"));
responseItem.setDesc(rs.getString("desc"));
responseItems.add(responseItem);
}
log.info("TestRowMapper items ="+responseItems);
return responseItems;
}
}
How to perform Spring validation in MultiActionController?
Let's write the following one
public class Person {
private String name;
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And your MultiActionController
import static org.springframework.validation.ValidationUtils.*;
#Component
public class PersonController extends MultiActionController {
public PersonController() {
setMethodNameResolver(new InternalPathMethodNameResolver());
setValidators(new Validator[] {new Validator() {
public boolean supports(Class clazz) {
return clazz.isAssignableFrom(Person.class);
}
public void validate(Object command, Errors errors) {
rejectIfEmpty(errors, "age", "", "Age is required");
rejectIfEmptyOrWhitespace(errors, "name", "", "Name is required");
}
}});
}
public ModelAndView add(HttpServletRequest request, HttpServletResponse response, Person person) throws Exception {
// do something (save our Person object, for instance)
return new ModelAndView();
}
}
MultiActionController defines a property called validators where you should provide any Validator used by your MultiActionController. Here you can see a piece of code which is responsible for validating your Command object inside MultiActionController
ServletRequestDataBinder binder = ...
if (this.validators != null)
for (int i = 0; i < this.validators.length; i++) {
if (this.validators[i].supports(command.getClass())) {
ValidationUtils.invokeValidator(this.validators[i], command, binder.getBindingResult());
}
}
}
/**
* Notice closeNoCatch method
*/
binder.closeNoCatch();
closeNoCatch method says
Treats errors as fatal
So if your Validator returns any Error, closeNoCatch will throw a ServletRequestBindingException. But, you can catch it inside your MultiActionController method, as follows
public ModelAndView hanldeBindException(HttpServletRequest request, HttpServletResponse response, ServletRequestBindingException bindingException) {
// do what you want right here
BindException bindException = (BindException) bindingException.getRootCause();
return new ModelAndView("personValidatorView").addAllObjects(bindException.getModel());
}
In order to test, let's do the following one
#Test
public void failureValidation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("POST");
request.setRequestURI("http://127.0.0.1:8080/myContext/person/add.html");
/**
* Empty values
*/
request.addParameter("name", "");
request.addParameter("age", "");
PersonController personController = new PersonController();
ModelAndView mav = personController.handleRequest(request, new MockHttpServletResponse());
BindingResult bindingResult = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + "command");
/**
* Our Validator rejected 2 Error
*/
assertTrue(bindingResult.getErrorCount() == 2);
for (Object object : bindingResult.getAllErrors()) {
if(object instanceof FieldError) {
FieldError fieldError = (FieldError) object;
System.out.println(fieldError.getField());
}
}
}