How to use criteria Query filter in azure cosmos DB - java

I am trying to use Criteria Query for Azure cosmosDB , but I am getting blank return everytime. I am trying to pass a model which will contain 5 parameters and some of them will be null while passing the model to the request method , here is the code which i am trying to use
Map map = new Map();
if(p.getA()!=null || p.getB() !=null || p.getC()!=null || p.getD()!=null){
map.put(p.getA()); //any of them can be null while passing
map.put(p.getB());//any of them can be null while passing
map.put(p.getC());//any of them can be null while passing
map.put(p.getD());//any of them can be null while passing
}
Criteria criteria = Criteria.getInstance(CriteriaType.ARRAY_CONTAINS,"TableA",Collections.singletonList(map),Part.IgnoreCaseType.ALWAYS);
CosmosQuery cosmos = new CosmosQuery(criteria).with(Sort.unsorted());
Iterable<BaseTable> items = cosmosTemplate.find(cosmos,BaseTable.class,"TableA");
Now this BaseTable contains all the table fields and TableA is extending that
public class TableA extends BaseTable{
}
BaseTable
public class BaseTable{
#Id
public String id;
public String A;
public String B;
public String C;
...............
}
Can anyone please tell me why every time i am getting null in the items, what is missing in the Criteria.

Related

How to update only particular fields in an entity using springbooot JPA update API

Here is my code.
Entit class:
#Entity
public class Book
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long bookId;
private double bookPrice;
private String bookTitle;
private String bookAuthor;
private String bookLanguage;
private LocalDate publicationDate;
private String publisher;
private byte[] bookImage;
private long isbn;
private int bookQuantity;
Controller class:
#PutMapping("/updatebooks")
public ResponseEntity<ApiResponse> updateBook(#RequestBody BookDto bookDto)
throws DataNotFoundException
{
return ResponseEntity.ok(bookService.updateBook(bookDto));
}
Service class:
#Override
public ApiResponse updateBook(BookDto bookDto) throws DataNotFoundException
{
Book book = bookRepository.findById(bookDto.getBookId())
.orElseThrow(() -> new DataNotFoundException("Book not found"));
book.setBookAuthor(bookDto.getBookAuthor());
book.setBookLanguage(bookDto.getBookLanguage());
book.setBookPrice(bookDto.getBookPrice());
book.setBookTitle(bookDto.getBookTitle());
book.setIsbn(bookDto.getIsbn());
book.setPublicationDate(bookDto.getPublicationDate());
book.setPublisher(bookDto.getPublisher());
bookRepository.save(book);
return new ApiResponse(HttpStatus.OK.value(), "Updation successful");
}
So through postman I just want to update bookAuthor field alone and other fields has to be same as it is in the database. But when I update just one field the others are automatically assigned as null and I just want to update only one field.
Here see that i'm just updating the bookAuthor field but others are simply changing to null. So how can I update only particular fields and display the others as as it is in database.
Pre Updation DB:
Post Updation DB:
This is not really springboot jpa problem. The issue is in the way updateBook method in service has been implemented. The updateBook method is setting all the fields from dto on persisted book entity without checking if the dto.get* returns null or not.
Very simply but crude solution to this problem would be to have if(dto.get* != null) check for each field before setting the value in book entity. i.e.
if (bookDto.getBookAuthor() != null)
book.setBookAuthor(bookDto.getBookAuthor());
if (bookDto.getBookLanguage() != null)
book.setBookLanguage(bookDto.getBookLanguage());
if (bookDto.getBookPrice() != 0)
book.setBookPrice(bookDto.getBookPrice());
...
This leaves the updateBook service method generic to allow updating one or more fields without worrying about others being overwritten. If this makes your service method too noisy the dto to entity conversation part can be extracted into its own method for clarity.
For more advance usecases and if your entity/dto has more than a handful fields or nested objects as fields then this becomes cumbersome. In such scenarios you may want to handcraft a separate implementation which perhaps uses reflection to map fields from dto to entity if the field value is not null. The usage of reflection is however likely to be slow and/or error prone if not done properly.
There are however libraries and frameworks for more regular usecases which makes such conversation easier. At the simplest springframework's BeanUtils.copyProperties method provides a way to map values from one object to another and the last argument of ignored properties can be used to provide fields names to be ignored. This stackoverflow answer shows how to write a generic method that creates a list of property names to ignore if they are null in source object; and they uses this list to pass as parameter in BeanUtils.copyProperties.
public static String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for(PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
return emptyNames.toArray(new String[0]);
}
then in your service method;
Book book = bookRepository.findById(bookDto.getBookId())
.orElseThrow(() -> new DataNotFoundException("Book not found"));
BeanUtils.copyProperties(bookDto, book, getNullPropertyNames(bookDto));
bookRepository.save(book);
For further advanced usecases you can use frameworks such as mapstruct or modelmapper.
Don't get the fields from the DTO. Just Do findByID, set the new bookAuthor to the entity and save.
#Modifying
#Query(nativeQuery = true, value = "update book set book_author = :bookAuthor where id = :bookId")
int updateBookAuthor(#Param("bookAuthor") String bookAuthor, #Param("bookId") Long bookId);
You can do something like this in your BookRepository, then invoke from the service class like...
bookRepository.updateBookAuthor(bookAuthor, bookId)
Or modify your service class method like the following...
#Override
public ApiResponse updateBook(BookDto bookDto) throws DataNotFoundException
{
Book book = bookRepository.findById(bookDto.getBookId())
.orElseThrow(() -> new DataNotFoundException("Book not found"));
book.setBookAuthor(bookDto.getBookAuthor());
bookRepository.save(book);
return new ApiResponse(HttpStatus.OK.value(), "Updation successful");
}

BeanPropertyRowMapper when mapping for field not available

I have a repository class that calls jdbcTemplate query() and uses BeanPropertyRowMapper(..)
#Override
public List<Model> query(final Model model) {
return this.jdbcTemplate.query(
this.QUERY.replace("${WHERE}", this.queryBuilder.build(model)),
new PostGrePropertyMapper(model)
.addProperty("test", new TestMapper().apply(model.getTest())),
new BeanPropertyRowMapper<>(Model.class));
}
Say I have this model (it has many fields but for demo shortened to 3):
public class Model {
private String test;
private String id;
private String reference;
}
and the query returns me two columns: test & id in the ResultSet.
Is there a way I can set the value of the reference field to that of id that is being returned from database? The id and reference should be set off the id field coming from DB.
Where can I set this value for reference without having to write a custom row mapper and setting each and every field with rs.getString(...) calls.
Is there a short technique for such scenario?
The shortest technique is to fix the query so that it matches the model:
SELECT test, id, id as reference FROM ...

Include code in getter while using Criteria + projection

I am having this getter in an Entity :
public String getEmpType() {
return empType == null ? "EMP" : empType;
}
and I am using Criteria + projection in the Dao :
Criteria criteria = session.createCriteria(Employee.class);
criteria.add(Restrictions.eq("employeeKey",key))
.setProjection(Projections.projectionList().add(Projections.property("empType")));
Now I want to apply the same rule in getter (i.e. if empType is null set it to EMP) without duplicating the code here .. is there a way to do so ?
i.e I want the Criteria to use the getter in the entity
note : I am using hibernate 3.1.0
Create a class with a static method that you can use in both cases like
public class EmpTypeHelper {
public static String getEmpTypeOrDefault(String emptType) {
return empType == null ? "EMP" : empType;
}
}

Calling a Stored procedure with SimpleJdbcCall returns null result set from Temp table. The stored procedure have no input and out put paramets

I have to call stored procedures From java spring. The procedures doesn't have input and out put parameters. when I call stored procedure using SimpleJdbcCall, it returns null result. How can I fix this Problem? Thank you
Stored Procedure
ALTER PROCEDURE "DBA"."Records" ()
RESULT ( Number integer
, Alias varchar(255)
, File varchar(255)
, ConnCount integer
, PageSize integer
, LogName varchar(255)
)
BEGIN
DECLARE dbid integer;
DECLARE local temporary table t_db_info
( Number integer null
, Alias varchar(255) null
, File varchar(255) null
, ConnCount integer null
, PageSize integer null
, LogName varchar(255) null
) on commit delete rows;
set dbid=next_database(null);
lbl: loop
if dbid is null then
leave lbl
end if
;
insert into t_db_info values(
dbid,db_property('Alias',
dbid),db_property('File',
dbid),db_property('ConnCount',
dbid),db_property('PageSize',
dbid),db_property('LogName',
dbid));
set dbid=next_database(dbid)
end loop lbl
;
select * from t_db_info
ORDER BY alias
END
Java Code
public class Generic <T>{
protected NamedParameterJdbcTemplate namedJdbcTemplate;
protected JdbcTemplate jdbcTemplate;
protected SimpleJdbcCall procReadAllorgs;
public GenericDAO(DataSource ds) {
this.procReadAllorgs = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("DBA.Records")
.returningResultSet("records",
BeanPropertyRowMapper.newInstance(Records.class));
init();
}
}
DAO Impl class
public class OrganDAOImpl implements Generic <T> {
public List getList() {
Map m = procReadAllorgs.execute(new HashMap<String, Object>(0));
return (List) m.get("records");
}
}
Controller class
#RequestMapping("/org")
public class TestController {
#Autowired
protected OrganDAOImpl organDAOImpl;
#RequestMapping(value = "/sunday", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody List getAll() {
logger.debug("calling the controller...");
return OrganDAOImpl.getList();
}
}
Model class
public class Records {
private Integer Number;
private String Alias;
private String File;
private Integer ConnCount;
private Integer PageSize;
private String LogName;
//getter and setter
}
I don't think this procedure returning anything?!
It doesn't have an out parameter to return it, (I am not a plsql expert)
.withProcedureName("Records")
.withCatalogName("DBA")
.withSchemaName(schema);
two cases you will get a null from the DB.
1- if the call is wrong and you don't have exception handling/ try and catch
2- the procedure does not return anything.
Problem: How to get an output param back from simplejdbccall!! I'm using MSSQL Server with Java Spring Framework.
I checked my stored procedure and discovered I forgot to mark the #out_personId variable as output:
ALTER PROCEDURE [dbo].[spNewUser]( #in_firstName nvarchar(20), #in_lastName nvarchar(20),#in_addressLine1 nvarchar(50), #in_addressLine2 nvarchar(50), #in_addressLine3 nvarchar(50), #in_addressLine4 nvarchar(50),#in_dob datetime, #in_gender nvarchar(1), #in_email nvarchar(100), #in_mobileNo nvarchar(50), #in_landLineNo nvarchar(50),#out_personId intoutput)
After this change (in bold) I was able to read the returned int value via SimpleJDBCCall. Here is the code sample:
public Person createPerson(final Person person) {
SimpleJdbcCall storedProcedure = new SimpleJdbcCall(dataSource)
.withProcedureName("spNewUser")
.withReturnValue();
SqlParameterSource params = new MapSqlParameterSource()
.addValue("in_firstName", person.getFirstName())
.addValue("in_lastName", person.getLastName())
.addValue("in_addressLine1", person.getAddressLine1())
.addValue("in_addressLine2", person.getAddressLine2())
.addValue("in_addressLine3", person.getAddressLine3())
.addValue("in_addressLine4", person.getAddressLine4())
.addValue("in_dob", new java.sql.Date(person.getDob().getTime()))
.addValue("in_gender", person.getGender())
.addValue("in_email", person.getEmail())
.addValue("in_mobileNo", person.getMobileNo())
.addValue("in_landLineNo", person.getLandLineNo())
.addValue("out_personId", 0);
Map<String, Object> output = storedProcedure.execute(params);
Integer id = (Integer) output.get("out_personId");
person.setId(id);
return person;
}

JPA: update only specific fields

Is there a way for updating only some fields of an entity object using the method save from Spring Data JPA?
For example I have a JPA entity like this:
#Entity
public class User {
#Id
private Long id;
#NotNull
private String login;
#Id
private String name;
// getter / setter
// ...
}
With its CRUD repo:
public interface UserRepository extends CrudRepository<User, Long> { }
In Spring MVC I have a controller that get an User object for update it:
#RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> updateUser(#RequestBody User user) {
// Assuming that user have its id and it is already stored in the database,
// and user.login is null since I don't want to change it,
// while user.name have the new value
// I would update only its name while the login value should keep the value
// in the database
userRepository.save(user);
// ...
}
I know that I could load the user using findOne, then change its name and update it using save... But if I have 100 fields and I want to update 50 of them it could be very annoying change each value..
Is there no way to tell something like "skip all null values when save the object"?
I had the same question and as M. Deinum points out, the answer is no, you can't use save. The main problem being that Spring Data wouldn't know what to do with nulls. Is the null value not set or is it set because it needs to be deleted?
Now judging from you question, I assume you also had the same thought that I had, which was that save would allow me to avoid manually setting all the changed values.
So is it possible to avoid all the manuel mapping then? Well, if you choose to adhere to the convention that nulls always means 'not set' and you have the original model id, then yes.
You can avoid any mapping yourself by using Springs BeanUtils.
You could do the following:
Read the existing object
Use BeanUtils to copy values
Save the object
Now, Spring's BeanUtils actual doesn't support not copying null values, so it will overwrite any values not set with null on the exiting model object. Luckily, there is a solution here:
How to ignore null values using springframework BeanUtils copyProperties?
So putting it all together you would end up with something like this
#RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> updateUser(#RequestBody User user) {
User existing = userRepository.read(user.getId());
copyNonNullProperties(user, existing);
userRepository.save(existing);
// ...
}
public static void copyNonNullProperties(Object src, Object target) {
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}
public static String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for(java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
Using JPA you can do it this way.
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class);
Root<User> root = criteria.from(User.class);
criteria.set(root.get("lastSeen"), date);
criteria.where(builder.equal(root.get("id"), user.getId()));
session.createQuery(criteria).executeUpdate();
You are able to write something like
#Modifying
#Query("update StudentXGroup iSxG set iSxG.deleteStatute = 1 where iSxG.groupId = ?1")
Integer deleteStudnetsFromDeltedGroup(Integer groupId);
Or If you want to update only the fields that were modified you can use annotation
#DynamicUpdate
Code example:
#Entity
#Table(name = "lesson", schema = "oma")
#Where(clause = "delete_statute = 0")
#DynamicUpdate
#SQLDelete(sql = "update oma.lesson set delete_statute = 1, "
+ "delete_date = CURRENT_TIMESTAMP, "
+ "delete_user = '#currentUser' "
+ "where lesson_id = ?")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
If you are reading request as JSON String, this could be done using Jackson API. Here is code below. Code compares an existing POJO Elements and create new one with updated fields. Use the new POJO to persist.
public class TestJacksonUpdate {
class Person implements Serializable {
private static final long serialVersionUID = -7207591780123645266L;
public String code = "1000";
public String firstNm = "John";
public String lastNm;
public Integer age;
public String comments = "Old Comments";
#Override
public String toString() {
return "Person [code=" + code + ", firstNm=" + firstNm + ", lastNm=" + lastNm + ", age=" + age
+ ", comments=" + comments + "]";
}
}
public static void main(String[] args) throws JsonProcessingException, IOException {
TestJacksonUpdate o = new TestJacksonUpdate();
String input = "{\"code\":\"1000\",\"lastNm\":\"Smith\",\"comments\":\"Jackson Update WOW\"}";
Person persist = o.new Person();
System.out.println("persist: " + persist);
ObjectMapper mapper = new ObjectMapper();
Person finalPerson = mapper.readerForUpdating(persist).readValue(input);
System.out.println("Final: " + finalPerson);
}}
Final output would be, Notice only lastNm and Comments are reflecting changes.
persist: Person [code=1000, firstNm=John, lastNm=null, age=null, comments=Old Comments]
Final: Person [code=1000, firstNm=John, lastNm=Smith, age=null, comments=Jackson Update WOW]
skip all null values when save the object
As others have pointed out, there is not straight forward solution in JPA.
But thinking out of the box you can use MapStruct for that.
This means you use the right find() method to get the object to update from the DB, overwrite only the non-null properties and then save the object.
You can use JPA as you know and just use MapStruct like this in Spring to update only the non-null properties of the object from the DB:
#Mapper(componentModel = "spring")
public interface HolidayDTOMapper {
/**
* Null values in the fields of the DTO will not be set as null in the target. They will be ignored instead.
*
* #return The target Holiday object
*/
#BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
Holiday updateWithNullAsNoChange(HolidayDTO holidayDTO, #MappingTarget Holiday holiday);
}
See the MapStruct docu on that for details.
You can inject the HolidayDTOMapper the same way you do it with other beans (#Autowired, Lombok,...) .
the problem is not spring data jpa related but the jpa implementation lib that you are using related.
In case of hibernate you may have a look at:
http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/
For long,int and other types;
you can use the following code;
if (srcValue == null|(src.getPropertyTypeDescriptor(pd.getName()).getType().equals(long.class) && srcValue.toString().equals("0")))
emptyNames.add(pd.getName());

Categories