Include code in getter while using Criteria + projection - java

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

Related

How to use criteria Query filter in azure cosmos DB

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.

Build WHERE clause dynamically based on filter in queryDSL

I have a query that needs a dynamic SQL WHERE clauses based on filters.
Issue
It may give me a NullPointerException (NPE) if I'm adding an "AND" while the previous filter is not existing.
Code
public List<BlocageDeblocageCompte> getMyQuery(FiltersDTO filters) {
JPAQuery<MY_ENTITY> myEntity = getJPAQueryFactory().selectFrom(myEntity).limit(20);
BooleanExpression whereClause = null;
boolean whereClauseAdded = false;
boolean ifNoFilter = Stream.of(myEntity).allMatch(Objects::isNull);
if (ifNoFilter) {
return new ArrayList<>(myEntity.fetchAll().fetch());
}
if (filters.getId() != null) {
whereClause = myEntity.id.eq(filters.getId());
whereClauseAdded = true;
}
if (filters.getName() != null) {
if (whereClauseAdded) {
whereClause = whereClause.and(myEntity.id.eq(filters.getName()));
} else {
whereClause = myEntity.id.eq(filters.getName());
whereClauseAdded = true;
}
}
// remaining code
}
Question
Is there a better way to add the filters, without risking a NPE?
To construct complex boolean queries like this you can use com.querydsl.core.BooleanBuilder:
public List<MyEntity> getMyQuery(FiltersDTO filters) {
MyEntity myEntity = QModel.myEntity;
// build an optional WHERE clause with predicates using AND conjunctions
BooleanBuilder builder = new BooleanBuilder();
if (filters.getId() != null) {
builder.and(myEntity.id.eq(filters.getId()));
}
if (filters.getName() != null) {
builder.and(myEntity.id.eq(filters.getName()));
}
// construct the query
return getJPAQueryFactory().selectFrom(myEntity)
.limit(20)
.where(builder) // myEntity.id eq id1 AND myEntity.name eq name1
.fetch();
}
See also
Similar questions:
Dynamic search term SQL query with Spring JPA or QueryDSL
JPA QueryBuilder
How to write JPA query with boolean condition
References:
QueryDSL Reference Guide: 3.1.1. Complex predicates
QueryDSL Issue on GitHub: BooleanBuilder with multiple AND conditions · Discussion #2936
Baeldung's Tutorial: A Guide to Querydsl with JPA
You can implement the Specification interface in JPA:
first of all, create a Filter class with static predicate-methods to handle fields in WHERE clause:
public class Filter{
public static Specification<BlocageDeblocageCompte> hasName(String name) {
return (root, query, cb) -> {
if (name == null || name.equals("")) {
return cb.isTrue(cb.literal(true)); // always true = no filtering
}
return cb.like(root.get("name"), "%" + name + "%");
};
}
public static Specification<ActiveAlarm> hasId(Integer id) {
return (root, query, cb) -> {
if (id == null) {
return cb.isTrue(cb.literal(true)); // always true = no filtering
}
return cb.equal(root.get("id"), id);
};
}
}
then, use the Filter class in your methods:
repository.findAll(where(Filter.hasName(filters.getName()).and(Filter.hasId(filters.getId()));
For more details, see:
Baeldung:
REST Query Language with Spring Data JPA Specifications
Spring blog: Advanced Spring Data JPA - Specifications and Querydsl
Kailas Nath on Medium: Using JPA Specification Interface To Implement Dynamic Filters
DZone: Java: Using the Specification Pattern With JPA
I found out a simple solution, when we do WHERE on all of them it transform automatically the second where to AND. thank u guys.
JPAQuery<MyEntity> myEntityJPAQUERY=
getJPAQueryFactory()
.selectFrom(QmyEntity);
if (filtresDTO.getId() != null) {
myEntityJPAQUERY=myEntityJPAQUERY.where(QmyEntity.id.eq(ffiltresDTO.getId()));
if (filtresDTO.getName() != null) {
myEntityJPAQUERY=myEntityJPAQUERY.where(QmyEntity.name.eq(ffiltresDTO.getName()))
}

How to anotate the attribute in the Entity if the table attribute has a default value of clock_timestamp()?

On the Postgres database, I have a table with "insert_time" attribute (timestamptz), which has a default value of clock_timestamp() function.
In Java, I have created the Entity from the table but when I like to insert a new record into the table the insert_time has a value of null instead of clock_timestamp().
How to properly annotate the Entity so if the insert_time is not passed in Java that a "default value" of clock_timestamp() is used on a table itself?
or how to read clock_timestamp() in the entity if the attribute is set to null.
thanks
You can't simply annotated the JPA entity and get the value returned by the function clock_timestamp()
In order to get that value to your code you will need to do a query
SELECT clock_timestamp();
But you can use JPA-Listeners to achieve something similar:
public class TimestampEntityListener {
#PrePersist
public void setDefaultTime(BaseEntity entity) {
if(entity.getInsertTime() == null) {
entity.setInsertTime(new Timestamp((new Date()).getTime())):
}
}
}
#MappedSuperclass
#EntityListeners({TimestampEntityListener.class})
public abstract class BaseEntity {...}
Now, if you're really interested in having the DB control the time of the insertion, and not delegate your application to do it, you can modify the above code and wire an entityManager in it so that you can perform that select against the DB and just set that value directly.
Update: on how to delegate the entityListener to query the Database
public class TimestampEntityListener {
#Autowired
EntityManager entityManager;
#PrePersist
public void setDefaultTime(BaseEntity entity) {
AutowireHelper.autowire(this, this.entityManager);
if(entity.getInsertTime() == null) {
Timestamp timestamp = (Timestamp)entityManager.createNativeQuery("select clock_timestamp()").getSingleResult();
entity.setInsertTime(timestamp):
}
}
}
You can use this class for AutowireHelper and with the above code it should work out of the box as long as your entities will extend the BaseEntity.
Please try this :
#PrePersist
public void setDefaultTime(BaseEntity entity) {
if(entity.getInsertTime() == null) {
entity.setInsertTime(new Timestamp((new Date()).getTime())):
}
}

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());

Hibernate: Criterion. Add Alias to Criterion Object

In Hibernate, is there a way to create an add an Alias to a Criterion object. I have the following to work with:
I have a dynamic search from big Database with many tables. The search has many (25+) optional non-exclusive parameters selected clien-side. This requires the use of the Hibernate Criteria API for managability. In my DAO I have the following method:
Public List<myPojoClass> getDataByCriterion( List<Criterion> restrictionList) {
Session s = HibernateUtil.currentSession();
Criteria c = s.createCriteria(myPojo.class);
for (Criterion crit : restrictionList){
c.add(crit);
}
List<myPojoClass> response = c.list();
return response;
}
I need to do a Join with myOtherPojo.class and would like to know if it is possible to add an alias to the Criteria list above.
Somthing like :
restrictionsList.add(... ...createAlias("myOtherPojo.class" , "mop");
then, I need o add other Logical and to this class as above.
You again! ;)
You could pass a collection of entries (like a HashMap<String, String>) and iterate over them to populate your aliases... like this:
Public List<myPojoClass> getDataByCriterion( List<Criterion> restrictionList, HashMap<String,String> aliases) {
Session s = HibernateUtil.currentSession();
Criteria c = s.createCriteria(myPojo.class);
for (Criterion crit : restrictionList){
c.add(crit);
}
for (Entry<String, String> entry : aliases.entrySet()){
c.createAlias(entry.getKey(), entry.getValue());
}
List<myPojoClass> response = c.list();
return response;
}
You could do something similar if you want to change the fetch modes. Of course the calling method needs to know the data model so it can set up the aliases properly, otherwise you can get errors at runtime.
from what I know there is now way to create joins without instance of Criteria. I suggest you create some wrapper for criteria which would contain criteria and alias definition if necessary and then use Criteria as visitor (like from this Pattern)
interface CriterionWrapper {
void visit(Criteria c);
}
class OnlyCriterionWrapper implements CriterionWrapper {
private Criterion c;
public void visit(Criteria c){c.add(c);}
}
class CriterionWrapper implements CriterionWrapper{
private Criterion c;
private String whateverIsNeededToCreateAlias
public void visit(Criteria c){
c.createAlias(whateverIsNeededToCreateAlias);
c.add(c);
}
}
and then pass List as parameter to your getDataByCriterion() method

Categories