Spring Data JPA Query by Example with access to nested Objects Attributes - spring

I use Query by Example and want to know how I can find objects with certain properties in the nested objects.
A plan anyone?
Here is my example Code:
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("offer2product.id.productId", match -> match.exact()
);
Offer2ProductId id = new Offer2ProductId();
id.setProductId(1337L);
Offer2Product offer2Product = new Offer2Product();
offer2Product.setId(id);
Set<Offer2Product> offer2productSet = new HashSet<>();
offer2productSet.add(offer2Product);
Offer probe = new Offer();
probe.setOffer2productSet(offer2productSet);
Example<Offer> example = Example.of(probe, matcher);
List<Offer> offerList = offerRepository.findAll(example);

Quoting Spring data documentation: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#query-by-example
Currently, only SingularAttribute properties can be used for property matching.
In your example, you want to search by a property that is a Set<> (offer2productSet), which is a PluralAttribute - it is not possible to search by this field. It will be ignored when building a query, as can be seen here:
https://github.com/spring-projects/spring-data-jpa/blob/master/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java#L112

man actually can do it, follow the example below, where demand has a list of labels, I don't have time to explain everything but soon I'll update this post.
#Repository
#RequiredArgsConstructor
public class DemandaFilterRepository {
private final EntityManager entityManager;
public Page<Demanda> findAll(DemandaFilterDTO demandaFilter, Pageable pageable) {
String query = "select d from Demanda d where 1=1";
Map<String, Object> parameters = new HashMap<>();
if (demandaFilter.getId() != null) {
query += " and d.id = :id";
parameters.put("id", demandaFilter.getId());
}
if (!StringUtils.isEmpty(demandaFilter.getTenant())) {
query += " and upper(d.tenant) = upper(:tenant)";
parameters.put("tenant", demandaFilter.getTenant());
}
if (!StringUtils.isEmpty(demandaFilter.getAssunto())) {
query += " and upper(d.assunto) like upper(:assunto)";
parameters.put("assunto", "%" + demandaFilter.getAssunto() + "%");
}
if (!StringUtils.isEmpty(demandaFilter.getDescricao())) {
query += " and upper(d.descricao) like upper(:descricao)";
parameters.put("descricao", "%" + demandaFilter.getDescricao() + "%");
}
if (!StringUtils.isEmpty(demandaFilter.getEtiqueta())) {
query = query.replace("Demanda d", "Demanda d inner join d.etiquetas etiqueta");
query += " and upper(etiqueta.descricao) = upper(:etiqueta)";
parameters.put("etiqueta", demandaFilter.getEtiqueta());
}
if (!StringUtils.isEmpty(demandaFilter.getNomeDemandante())) {
query += " and upper(d.demandante.nome) like upper(:nomeDemandante)";
parameters.put("nomeDemandante", "%" + demandaFilter.getNomeDemandante() + "%" );
}
if (!StringUtils.isEmpty(demandaFilter.getDtInclusao())) {
query += " d.dtInclusao like :dtInclusao";
parameters.put("dtInclusao", "%" + demandaFilter.getDtInclusao() + "%");
}
query += " ORDER BY d.id DESC ";
TypedQuery<Demanda> typedQuery = entityManager.createQuery(query, Demanda.class)
.setMaxResults(pageable.getPageSize())
.setFirstResult(pageable.getPageNumber() * pageable.getPageSize());
parameters.forEach(typedQuery::setParameter);
return new PageImpl<>(typedQuery.getResultList());
}
}
this is a class for one of a small project that I'm working on ...

Related

Adding parameters optionally to Spring Data JPA Native query

I am using Spring Data JPA with native queries like below
public interface ItemRepository extends JpaRepository<ItemEntity, Long> {
#Query(value = "select * from items i where i.category = :itemCategory and i.item_code = :itemCode", nativeQuery = true)
Page<ItemEntity> search(#Param("itemCategory") String itemCategory, #Param("itemCode") String itemCode, Pageable pageable);
}
Now, my use case is
It itemCode is available, only the item with that code from that category should be returned.
But if itemCode is not available then all items in that category should be returned.
So the problem with the above category is when itemCode is passed as NULL no data is returned since it does not match anything. whereas the requirement is it should be ignored.
So is there a way to optionally add a clause to Spring Data JPA native query. I know it is possible with CriteriaQuery but can we do something similar for native queries?
Thanks
Yes, it's feasible with native query too. Very Good explanation here read this
#Approach1
#NamedQuery(name = "getUser", query = "select u from User u"
+ " where (:id is null or u.id = :id)"
+ " And :username"
:itemCode is null or i.item_code = :itemCode
#Approach2
# UserDao.java
public User getUser(Long id, String usename) {
String getUser = "select u from user u where u.id " + Dao.isNull(id)
+ " And u.username " + Dao.isNull(username);
Query query = Dao.entityManager.createQuery(getUser);
}
# Dao.java
public static String isNull(Object field) {
if (field != null) {
if (field instanceof String) {
return " = " + "'" + field + "'";
} else {
return " = " + field;
}
} else {
return " is NULL ";
}
}
How to handle null value of number type in JPA named query
Just modify the where condition
i.item_code = :itemCode
to
:itemCode is null or i.item_code = :itemCode

Apache jena to update remote endpoint

The current procedure we now often perform is to extract data from an endpoint, perform computational analysis, generate RDF files and manually load them back into the endpoint.
Now I was looking into automating this procedure using Apache Jena ARQ as these dependencies are currently used for the retrieval of information.
I manage to get it partially to work using INSERT statements but performing thousands if not millions of inserts one by one seem a bit inefficient to me. The second issue is that we sometimes have regex or " in a string and this needs to escaped but there are many exceptions.
Is there a way to either parse or iterate over an internal apache jena model statements and inject this directly into an endpoint?
We currently use graphdb but it would be great if this can be applied using a universal approach.
Update
I have updated the code to handle 10 statements at once not sure yet what eventually the limit will be...
public class EndpointTests extends TestCase {
// Only for local testing
public void testEndpoint() throws Throwable {
String endpoint = "http://10.117.11.77:7200/repositories/Test";
Domain domain = new Domain("file:///Users/jasperkoehorst/diana_interproscan_head.nt");
StmtIterator statements = domain.getRDFSimpleCon().getModel().listStatements();
String strInsert = "INSERT DATA { ";
int insertCounter = 0;
while (statements.hasNext()) {
insertCounter = insertCounter + 1;
Statement statement = statements.nextStatement();
String subject = statement.getSubject().getURI();
String predicate = statement.getPredicate().getURI();
String object = statement.getObject().toString();
if (statement.getObject().isURIResource()) {
object = "<" + statement.getObject().toString() + ">";
}
if (statement.getObject().isLiteral()) {
object = statement.getObject().asLiteral().getString();
object = object.replaceAll("\\\\", "\\\\\\\\");
object = object.replaceAll("\"","\\\\\"");
}
if (object.startsWith("http")) {
object = "<" + object + ">";
} else {
object = "\"" + object + "\"";
}
strInsert = strInsert + "<" + subject + "> <" + predicate + "> " + object + " . ";
if (insertCounter % 10 == 0) {
System.out.println(insertCounter);
strInsert = strInsert + " } ";
UpdateRequest updateRequest = UpdateFactory.create(strInsert);
UpdateProcessor updateProcessor = UpdateExecutionFactory.createRemote(updateRequest, endpoint + "/statements");
updateProcessor.execute();
strInsert = "INSERT DATA { ";
}
}
}
}

Database column data having linebreaks are ignored when retrieved from Hibernate?

A CLOB database column data has linebreaks :
When I retrieve it and display the data inside a html table cell then the linebreaks are ignored :
#Override
#Transactional
public String getPrevisionRessourceAutreForProduit(Objectif produit) {
String hql = "select to_char(r.ress_comment_1) " +
"from ressource r join type_ressource t on r.type_ress_code = t.type_ress_code " +
"left join objectif o on r.obj_code = o.obj_code " +
"where o.obj_code = " + produit.getCode().toString() + " and upper(t.type_ress_code) = 'AUT'";
Session sessionDynamic = Utils.createDynamicSession(env);
Query query = sessionDynamic.createSQLQuery(hql);
#SuppressWarnings("unchecked")
List<String> list = (List<String>) query.list();
sessionDynamic.close();
if (list.isEmpty())
return "";
else
return list.get(0) == null ? "" : list.get(0);
}
So how to fix it ?
I found the solution by enclosing the data inside the tags <pre>...</pre>

How can I make a HQL using Instant from java.time

I am developing a small project using spring-jpa. I decided to use java.time to deal with date problems. Everything was working fine until I had to deal with HQL.
So I am trying to make this query:
#Query("SELECT sensor "
+ "FROM TrashSensor sensor "
+ "join sensor.measurementList measurement "
+ "WHERE sensor.id = 100 AND measurement.instantOfMeasure > '2017-01-01'")
public TrashSensor findTrashSensorByIdCustom();
But the type of measurement.instantOfMeasure is a Instant from java.time.
So this last AND of my WHERE clause is always returning true so I don't get the filter I need.
How can I make this comparison using HQL and java.time? Does Hibernate support this??
You can develop it with
1 way -> (in TrashSensorRepository interface )
#Query("SELECT sensor "
+ "FROM TrashSensor sensor "
+ "join sensor.measurementList measurement "
+ "WHERE sensor.id =:sensorId AND measurement.instantOfMeasure > :instantOfMeasure")
public TrashSensor findTrashSensorByIdCustom(#Param("sensorId") sensorId, #Param("instantOfMeasure") Instant instantOfMeasure);
The types from the java.time package are directly mapped to corresponding SQL types(Since Java 8)
LocalDate is mapped to DATE
LocalTime and OffsetTime are mapped to TIME
Instant, LocalDateTime, OffsetDateTime and ZonedDateTime are mapped to TIMESTAMP
2 way -> (if you have created custom utility class for run HQL dynamically)
// Utility for run custom dynamic HQL query
#Service
#Transactional
public class HibernateQueryService {
private final Logger log = LoggerFactory.getLogger(HibernateQueryService.class);
private JpaContext jpaContext;
public List executeHibernateQuery(String sql, Class entity){
log.debug("HibernateQuery executing: "+sql);
Session session = jpaContext.getEntityManagerByManagedType(Article.class).unwrap(Session.class);
return session.createQuery(sql, entity).getResultList();
}
}
// Custom converter
public class CustomConverter{
public static Timestamp toTimestamp(Instant time){
return time != null ? Timestamp.from(time) : null;
}
public static Instant toInstant(Timestamp timestamp){
return timestamp != null ? timestamp.toInstant() : null;
}
}
// Solution
public class Solution2{
public static makeIt(){
boolean isFirst = true;
String query = "SELECT sensor "
+ "FROM TrashSensor sensor "
+ "join sensor.measurementList measurement ";
if(sensor.id != null){
query += isFirst ? "WHERE " : "AND ";
query += "sensor.id="+sensor.id+" ";
isFirst = false;
}
if(measurement.instantOfMeasure != null){
query += isFirst ? "WHERE " : "AND ";
query += "measurement.instantOfMeasure > "+toTimestamp(measurement.instantOfMeasure)+" ";
}
List<TrashSensor> hibernateQueryService.executeHibernateQuery(query, TrashSensor.class);
}
}

Converting from dataset to list in linq

I have dataset function which is as below,
public DataSet GetUpodBrandList(string criteria, string locationId)
{
using (SqlConnection conn = new SqlConnection(this.ConnectionString))
{
string query;
if (criteria == "")
query = "select distinct brandDesc " +
"from arabia_upod_item_master " +
"where locationId = '" + locationId +
"' order by brandDesc";
else
query = "select distinct brandDesc " +
"from arabia_upod_item_master " +
"where brandDesc like '%" + criteria + "%' " +
"and locationId = '" + locationId + "'
order by brandDesc";
conn.Open();
SqlCommand command = new SqlCommand(query, conn);
return this.ExecuteDatasetStoredProc(command);
}
}
I am trying to convert it into linq as follow,
public static List<DataContext.arabia_upod_item_master> GetUpodBrandList(
string criteria,
string locationId)
{
List<DataContext.arabia_upod_item_master> m =
new List<DataContext.arabia_upod_item_master>();
using (var db = UpodDatabaseHelper.GetUpodDataContext())
{
db.ObjectTrackingEnabled = false;
if (criteria == "")
m = db.arabia_upod_item_masters.Where(
i => i.locationId == Convert.ToInt32(locationId))
.OrderBy(i => i.brandDesc)
.Distinct()
.ToList();
else
m = db.arabia_upod_item_masters.Where(
i => i.brandDesc.Contains(criteria) &&
i.locationId == Convert.ToInt32(locationId))
.OrderBy(i => i.brandDesc)
.Distinct()
.ToList();
return m;
}
}
But I don't know how to select distinct brandDesc in the above function (as in the previous function). I am simply using Distinct(). Is it right? or is there any other way to achieve it? Also, if there was 'case' in query in my old function (i.e the first one above) then how will i convert it to linq in the second function. Any other things to worry about while converting to linq?
Put a .Select(i => i.brandDesc) just before each Distinct call. You'll also need to change your List<x> so x is whatever the type of brandDesc is.
If I were to refactor you whole method, I'd do something like the following. I've pulled out code that is common to both forms of the query, and tweaked in a couple of other places.
public static IList<string/* I assume*/>GetUpodBrandList(
string criteria, string locationId)
{
// only do this once, not once per item.
int locatId = Convert.ToInt32(locationId);
using (var db = UpodDatabaseHelper.GetUpodDataContext())
{
db.ObjectTrackingEnabled = false;
// This is a common start to both versions of the query.
var query = db.arabia_upod_item_masters
.Where(i => i.locationId == locatId);
// This only applies to one version of the query.
if (criteria != "")
{
query = query.Where(i => i.brandDesc.Contains(criteria));
}
// This is a common end to both version of the query.
return query
.Select(i => i.brandDesc)
.Distinct()
// Do the ordering after the distinct as it will
// presumably take less effort?
.OrderBy(i => i.brandDesc)
.ToList();
}
}

Resources