hibernate subselect return null - spring

I'm using subselect in hibernate to return an object that contains id on all related table instead of object.
this the dto that I defined
#Entity
#Subselect("select di.id as id, user.id as userId, client.id as clientId, controller.id as controllerId,"
+ "supplier.id as supplierId, grade.id as gradeId, packing.id as packingId, warehouse.id as warehouseId,"
+ "qualityController.id as qualityControllerId,"
+ "companyMasterByPledger.id as pledgerId,"
+ "di.refNumber as refNumber,"
+ "di.clientRef as clientRef,"
+ "di.date as date,"
+ "di.supplierRef as supplierRef,"
+ "di.tons as tons,"
+ "di.kgPerBag as kgPerBag,"
+ "di.noOfBags as noOfBags,"
+ "di.deliveryDate as deliveryDate,"
+ "di.fromTime as fromTime,"
+ "di.toTime as toTime,"
+ "di.markingOnBags as markingOnBags,"
+ "di.originId as originId,"
+ "di.qualityId as qualityId,"
+ "di.remarks as remarks,"
+ "di.status as status,"
+ "di.log as log "
+ "from DeliveryInstruction as di "
+ "left join di.user as user "
+ "left join di.companyMasterByClientId as client "
+ "left join di.companyMasterByWeightControllerId as controller "
+ "left join di.companyMasterBySupplierId as supplier "
+ "left join di.gradeMaster as grade "
+ "left join di.packingMaster as packing "
+ "left join di.companyMasterByQualityControllerId as qualityController "
+ "left join di.companyMasterByPledger as pledger "
+ "left join di.warehouse as warehouse")
#Synchronize({"DeliveryInstruction"})
public class DeliveryView implements Serializable{
private Integer id;
private Integer userId;
private Integer clientId;
private Integer controllerId;
private Integer supplierId;
private Integer gradeId;
private Integer packingId;
private Integer warehouseId;
private Integer qualityControllerId;
private Integer pledgerId;
private String refNumber;
private String clientRef;
private Date date;
private String supplierRef;
private Double tons;
private Float kgPerBag;
private Integer noOfBags;
private Date deliveryDate;
private String fromTime;
private String toTime;
private String markingOnBags;
private Integer originId;
private Integer qualityId;
private String remark;
private Byte status;
private String log;
public DeliveryView() {
}
public DeliveryView(Integer id, Integer userId, Integer clientId, Integer controllerId, Integer supplierId, Integer gradeId, Integer packingId, Integer warehouseId, Integer qualityControllerId, Integer pledgerId, String refNumber, String clientRef, Date date, String supplierRef, Double tons, Float kgPerBag, Integer noOfBags, Date deliveryDate, String fromTime, String toTime, String markingOnBags, Integer originId, Integer qualityId, String remark, Byte status, String log) {
this.id = id;
this.userId = userId;
this.clientId = clientId;
this.controllerId = controllerId;
this.supplierId = supplierId;
this.gradeId = gradeId;
this.packingId = packingId;
this.warehouseId = warehouseId;
this.qualityControllerId = qualityControllerId;
this.pledgerId = pledgerId;
this.refNumber = refNumber;
this.clientRef = clientRef;
this.date = date;
this.supplierRef = supplierRef;
this.tons = tons;
this.kgPerBag = kgPerBag;
this.noOfBags = noOfBags;
this.deliveryDate = deliveryDate;
this.fromTime = fromTime;
this.toTime = toTime;
this.markingOnBags = markingOnBags;
this.originId = originId;
this.qualityId = qualityId;
this.remark = remark;
this.status = status;
this.log = log;
}
#Id
public Integer getId() {
return id;
}
// ... others getter and setter
}
and in the DAO class, the method looks like below
public DeliveryView getDiById(int id) {
return (DeliveryView) getHibernateTemplate().get(DeliveryView.class, id);
}
However when I use the above method, it returned null.
When I run the method, the script that is printed to the console is
select deliveryvi0_.id as id36_0_, deliveryvi0_.clientId as clientId36_0_, deliveryvi0_.clientRef as clientRef36_0_, deliveryvi0_.controllerId as controll4_36_0_, deliveryvi0_.date as date36_0_, deliveryvi0_.deliveryDate as delivery6_36_0_, deliveryvi0_.fromTime as fromTime36_0_, deliveryvi0_.gradeId as gradeId36_0_, deliveryvi0_.kgPerBag as kgPerBag36_0_, deliveryvi0_.log as log36_0_, deliveryvi0_.markingOnBags as marking11_36_0_, deliveryvi0_.noOfBags as noOfBags36_0_, deliveryvi0_.originId as originId36_0_, deliveryvi0_.packingId as packingId36_0_, deliveryvi0_.pledgerId as pledgerId36_0_, deliveryvi0_.qualityControllerId as quality16_36_0_, deliveryvi0_.qualityId as qualityId36_0_, deliveryvi0_.refNumber as refNumber36_0_, deliveryvi0_.remark as remark36_0_, deliveryvi0_.status as status36_0_, deliveryvi0_.supplierId as supplierId36_0_, deliveryvi0_.supplierRef as supplie22_36_0_, deliveryvi0_.toTime as toTime36_0_, deliveryvi0_.tons as tons36_0_, deliveryvi0_.userId as userId36_0_, deliveryvi0_.warehouseId as warehou26_36_0_ from DeliveryView deliveryvi0_ where deliveryvi0_.id=?
it gets the data from DeliveryView table which does not exist, what I want is to get the data from DeliveryInstruction table. Please help me to correct it, thanks
P/s: I'm using spring and hibernate and I do this way to work with jackson to prevent it load a lot of redundant information (I used jackson hibernate module, but it returned a lot of unneccessary information, instead of only id). So if you have any better idea, please tell me, thanks.
Update: I saw that the script on #subselect didn't run, it execute the default script "select * from deliveryView" when I call "get(DeliveryView.class, id)" method.
Update: This my native script that I've checked
select di.id as id, user.id as userId, client.id as clientId, controller.id as controllerId,
supplier.id as supplierId, grade.id as gradeId, packing.id as packingId, warehouse.id as warehouseId,
qualityController.id as qualityControllerId,
pledger.id as pledgerId,
di.ref_number as refNumber,
di.client_ref as clientRef,
di.date as date,
di.supplier_ref as supplierRef,
di.tons as tons,
di.kg_per_bag as kgPerBag,
di.no_of_bags as noOfBags,
di.delivery_date as deliveryDate,
di.from_time as fromTime,
di.to_time as toTime,
di.marking_on_bags as markingOnBags,
di.origin_id as originId,
di.quality_id as qualityId,
di.remark as remarks,
di.status as status,
di.log as log
from delivery_instruction di
left join user on user.id = di.user_id
left join company_master client on client.id = di.client_id
left join company_master controller on controller.id = di.weight_controller_id
left join company_master supplier on supplier.id = di.supplier_id
left join grade_master grade on grade.id = di.grade_id
left join packing_master packing on packing.id = di.packing_id
left join company_master qualityController on qualityController.id = di.quality_controller_id
left join company_master pledger on pledger.id = di.pledger
left join warehouse on warehouse.id = di.warehouse_id
where di.id = 21

1 Create simple class(without annotations) that holds all properties you need with getters and setters
2 Execute native sql:
public DeliveryView getDiById(int id) {
DeliveryView dV = (DeliveryView) sessionFactory.getCurrentSession()
.createSQLQuery(yourQueryHere).setResultTransformer(
new AliasToBeanResultTransformer(DeliveryView.class)).uniqueResult();
return dV;
}

Related

How to map dynamic query to projection interface. not an entity class

#Entity
#Table(name = "delivery_status_summary", schema = "dsd")
#Data
public class DeliveryStatusSummaryV2 {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="delivery_status_summary_id_seq")
#SequenceGenerator(name="delivery_status_summary_id_seq", sequenceName="delivery_status_summary_id_seq", allocationSize=1)
private Integer id;
#Column(name = "expected_delivery_date")
private LocalDateTime expectedDeliveryDate;
private Integer total;
#Column(name = "on_time")
private Integer onTime;
private Integer late;
private Integer pending;
#Column(name = "not_received")
private Integer notReceived;
}
Repository:
public interface DeliveryStatusSummaryRepository extends JpaRepository<DeliveryStatusSummary, Integer>, DeliveryStatusSummaryCustomRepository {
//language=SQL
String MANAGER_DELIVERY = "WITH dates AS (" +
" SELECT" +
" GENERATE_SERIES(" +
" CAST(:range_start AS TIMESTAMP)," +
" CAST(:range_end AS TIMESTAMP)," +
" INTERVAL '1 day'" +
" ) AS day" +
"), deliveries AS (" +
" SELECT *" +
" FROM dsd.delivery_status_summary AS dss" +
" WHERE dss.expected_delivery_date >= CAST(:range_start AS TIMESTAMP) AND" +
" dss.expected_delivery_date <= CAST(:range_end AS TIMESTAMP)" +
"), managers AS (" +
" SELECT *" +
" FROM dsd.teams as t" +
" WHERE t.manager_id IN (:manager_ids)" +
"), summary AS (" +
" SELECT t.manager_id," +
" dss.expected_delivery_date," +
" CAST(dss.expected_delivery_date AS DATE) AS day," +
" SUM(dss.total) AS total," +
" SUM(dss.on_time) AS on_time," +
" SUM(dss.late) AS late," +
" SUM(dss.pending) AS pending," +
" SUM(dss.not_received) AS not_received" +
" FROM deliveries AS dss" +
" INNER JOIN dsd.sla_datasets AS s ON dss.sla_id = s.sla_id" +
" INNER JOIN dsd.datasets AS ds ON ds.dataset_id = s.dataset_id" +
" INNER JOIN managers AS t on ds.team_id = t.ad_id" +
" INNER JOIN dsd.employee AS e on t.manager_id = e.id" +
" GROUP BY t.manager_id, dss.expected_delivery_date" +
")" +
"SELECT d.day AS expectedDeliveryDate, " +
"s.manager_id, " +
"COALESCE(s.total, 0) AS totalCount, " +
"COALESCE(s.on_time, 0) AS onTimeCount, " +
"COALESCE(s.late, 0) AS lateCount, " +
"COALESCE(s.pending, 0) AS pendingCount, " +
"COALESCE(s.not_received, 0) AS notReceivedCount " +
"FROM dates AS d " +
"LEFT JOIN summary AS s ON d.day = s.day " +
"ORDER BY s.manager_id, d.day";
#Query(value = MANAGER_DELIVERY, nativeQuery = true)
CompletableFuture<List<DeliveryStatusSummaryByManagerAndDate>> getDailyDeliveryStatusSummaryByManagers(
#Param("manager_ids") final Set<String> employeeIds,
#Param("range_start") ZonedDateTime rangeStart,
#Param("range_end") ZonedDateTime rangeEnd
);
}
Projection.
public interface DeliveryStatusSummaryByManagerAndDate {
String getManagerId();
LocalDate getExpectedDeliveryDate();
int getTotalCount();
int getOnTimeCount();
int getLateCount();
int getPendingCount();
int getNotReceivedCount();
}
getDailyDeliveryStatusSummaryByManagers works as expected.
But I need to have an opportunity to change group by section of this query on-the-fly, depending on user's input. So I decided to play with query as a plain string.
The idea is to put a query to string, and then depending on user's input, make query.replace('group placeholder', group by <needed list of fields>).
In order to archive this, I created a custom DeliveryStatusSummaryCustomRepository.
I decided not to make replace on a string so far, but to try to execute a simple query MANAGER_DELIVERY_QUERY.
public interface DeliveryStatusSummaryCustomRepository {
List<DeliveryStatusSummaryByManagerAndDate> getDailyDeliveryStatusSummaryByManagersV2();
}
#Repository
public class DeliveryStatusSummaryCustomRepositoryImpl implements DeliveryStatusSummaryCustomRepository {
private String MANAGER_DELIVERY_QUERY_TRY = "SELECT '1' AS managerId, " +
"CAST(dss.expected_delivery_date AS DATE) AS expectedDeliveryDate, " +
"0 AS totalCount, " +
"0 AS onTimeCount, " +
"0 AS lateCount, " +
"0 AS pendingCount, " +
"0 AS notReceivedCount " +
"FROM dsd.delivery_status_summary AS dss " +
"WHERE dss.expected_delivery_date >= CAST('01-01-2022' AS TIMESTAMP) AND dss.expected_delivery_date <= CAST('10-01-2022' AS TIMESTAMP)";
private final PrimaryDbConfig primaryDbConfig;
public DeliveryStatusSummaryCustomRepositoryImpl(PrimaryDbConfig primaryDbConfig) {
this.primaryDbConfig = primaryDbConfig;
}
#Override
public List<DeliveryStatusSummaryByManagerAndDate> getDailyDeliveryStatusSummaryByManagersV2() {
final LocalContainerEntityManagerFactoryBean em = primaryDbConfig.primaryEntityManager();
final EntityManager nativeEntityManager = em.createNativeEntityManager(new HashMap<>());
final Query query = nativeEntityManager.createQuery(MANAGER_DELIVERY_QUERY_TRY);
return query.getResultList();
}
}
But I got an error:
org.hibernate.hql.internal.ast.QuerySyntaxException: delivery_status_summary is not mapped
at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:169)
at org.hibernate.hql.internal.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:91)
at org.hibernate.hql.internal.ast.tree.FromClause.addFromElement(FromClause.java:77)
at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:333)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3765)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3654)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:737)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:593)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:330)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:144)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:113)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:73)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:162)
at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:613)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:725)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23)
at amp.ae.dataset.status.dashboard.library.repository.primary.DeliveryStatusSummaryCustomRepositoryImpl.getDailyDeliveryStatusSummaryByManagersV2(DeliveryStatusSummaryCustomRepositoryImpl.java:115)
at amp.ae.dataset.status.dashboard.library.repository.primary.DeliveryStatusSummaryCustomRepositoryImpl$$FastClassBySpringCGLIB$$f0397
I believe that the issue is related to DeliveryStatusSummaryByManagerAndDate is not entity, this is just projection.
If so, what approach to use for writing dynamic query taking into account that MANAGER_DELIVERY query is quite complicated to be re-written with Hibernate joins?
I heard about https://www.jooq.org.
Any advice appreciated.
You need to use EntityManager.createNativeQuery() method.
Also you can just autoware EntityManager
#Repository
public class DeliveryStatusSummaryCustomRepositoryImpl {
#PersistenceContext
private EntityManager entityManager;
}
To map query results to DTO
Replace the interface DeliveryStatusSummaryByManagerAndDate with POJO with getters and setters.
Use a result transformer from Hibernate
import org.hibernate.query.Query;
Query<DeliveryStatusSummaryByManagerAndDate> query = entityManager
.createNativeQuery(sql)
.unwrap(Query.class)
.setResultTransformer(
Transformers.aliasToBean(DeliveryStatusSummaryByManagerAndDate.class)
);
return query.getResultList();
Also check this mapping Hibernate query results to custom class?

How to get an intersect query using CriteriaQuery?

Given :
#Entity
public class entity_details {
#Id
private int id;
#column(name ="entity_id")
private int entityId;
#column(name="att_id")
private int attId;
#column(name="att_value")
private string attVal;
I want to find a equivalent query to the below one
select entity_id from entity_details where att_id=15 and att_value='test'
intersect
select entity_id from entity_details where att_id=18 and att_value='test2';
using CriteriaQuery only.
It is a complex operation, you could try something like this:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<EntityDetails> root = cq.from(EntityDetails.class);
Subquery<EntityDetails> sq = cq.subquery(EntityDetails.class);
Root<EntityDetails> rootSQ = cq.from(EntityDetails.class);
sq.where(
cb.and(
cb.and(
cb.equal(rootSQ.get("attId"),18),
cb.equal(rootSQ.get("attValue"),"test2")
),
cb.equal(rootSQ.get("id"),root.get("id"))
)
);
cq.where(
cb.and(
cb.and(
cb.equal(root.get("attId"),15),
cb.equal(root.get("attValue"),"test")
),
cb.exist(sq)
)
);
cq.multiselect(root.get("entityId"));
What would generate the following Query:
select e1.entity_id from entity_details e1
where e1.att_id = 15 and e1.att_value = "test"
and exists(
select * from entity_details e2
where e2.att_id = 18 and e2.att_value = "test2"
and e1.id = e2.id
)

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

Query works fine in MySql but returns no records with JdbcTemplate

I have the following service method:
public Page<ProductSalesReportLineDto> getReport(
Unit unit, Organization customer, Organization supplier, Date startDate, Date endDate, Pageable pageable
){
List<ProductSalesReportLineDto> lines = new ArrayList<ProductSalesReportLineDto>();
String sql = "SELECT product.id, product.code as code, product.barcode as barcode, product_name.name__text AS name, " +
"SUM(order_line.quantity) as quantity, SUM(order_line.commissioned_price_price * order_line.quantity) as price " +
"FROM product LEFT JOIN order_line ON order_line.product_id = product.id " +
"JOIN `order` ON order_line.order_id = order.id " +
"JOIN product_name ON product_name.product_id = product.id " +
"WHERE product_name.name__locale = \"en-US\" " +
"AND (:customer is null OR :customer='' OR `order`.customer_id = :customer) " +
"AND (:supplier is null OR :supplier='' OR `order`.supplier_id = :supplier) " +
"AND (:startDate is null OR :startDate='' OR `order`.date >= :startDate) " +
"AND (:endDate is null OR :endDate='' OR `order`.date <= :endDate) " +
"AND (:unit is null OR :unit='' OR product.unit = :unit) " +
"GROUP BY product.id, product_name.name__text " +
"LIMIT :offset, :pageSize";
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("offset", pageable.getOffset());
parameters.put("pageSize", pageable.getPageSize());
parameters.put("customer", customer != null ? customer.id : null);
parameters.put("supplier", supplier != null ? supplier.id : null);
parameters.put("startDate", startDate);
parameters.put("endDate", endDate);
parameters.put("unit", unit);
List<Map<String, Object>> rows = namedParameterJdbcTemplate.queryForList(sql, parameters);
for (Map<String, Object> row : rows) {
ProductSalesReportLineDto line = new ProductSalesReportLineDto();
line.productName = row.get("name") != null ? row.get("name").toString() : "";
line.productCode = row.get("code") != null ? row.get("code").toString() : "";
line.productBarcode = row.get("barcode") != null ? row.get("barcode").toString() : "";
line.quantity = row.get("quantity") != null ? Double.parseDouble(row.get("quantity").toString()) : 0;
line.price = row.get("price") != null ? Double.parseDouble(row.get("price").toString()) : 0;
lines.add(line);
}
Page<ProductSalesReportLineDto> page = new PageImpl<ProductSalesReportLineDto>(lines);
return page;
}
The parameters come from a #RestController method:
public #ResponseBody
ResponseEntity<?> get(
#RequestParam(value = "unit", required = false) Unit unit,
#RequestParam(value = "customer", required = false) Organization customer,
#RequestParam(value = "supplier", required = false) Organization supplier,
#DateTimeFormat(pattern = "yyyyy-MM-dd") #RequestParam(value = "startDate", required = false) Date startDate,
#DateTimeFormat(pattern = "yyyyy-MM-dd") #RequestParam(value = "endDate", required = false) Date endDate,
Pageable pageable
)
And Unit is an enum type field on Product:
#Enumerated(EnumType.STRING)
public Unit unit;
public enum Unit {
ROLL("ROLL", "ROLL", AbstractUnit.ONE),
METRE("METRE", Units.METRE.getSymbol(), Units.METRE),
KILOGRAM("KILOGRAM", Units.KILOGRAM.getSymbol(), Units.KILOGRAM),
PIECE("PIECE", "PIECE", AbstractUnit.ONE),
SAMPLE("SAMPLE", "SAMPLE", AbstractUnit.ONE);
I cannot retrieve products by unit using this query. With MySql the query works fine and it returns records. For example:
SELECT product.id, product.code as code, product.barcode as barcode, product_name.name__text AS name, SUM(order_line.quantity) as quantity, SUM(order_line.commissioned_price_price) as price FROM product LEFT JOIN order_line ON order_line.product_id = product.id JOIN `order` ON order_line.order_id = `order`.id JOIN product_name ON product_name.product_id = product.id WHERE product_name.name__locale = "en-US" AND (null is null OR null='' OR `order`.customer_id = null) AND (null is null OR null='' OR `order`.supplier_id = null) AND (null is null OR null='' OR `order`.date >= null) AND (null is null OR null='' OR `order`.date <= null) AND ("ROLL" is null OR "ROLL"='' OR product.unit = "ROLL") GROUP BY product.id, product_name.name__text
This query returns one row. When I run the same query via the rest interface and NamedParameterJdbcTemplate it returns no records. Can anybody tell what I am missing?
Following code also returns one row:
String sql2 = "SELECT * FROM product WHERE product.unit = :unit";
Map<String, Object> parameters2 = new HashMap<String, Object>();
parameters2.put("unit", "ROLL");
List<Map<String, Object>> rows2 = namedParameterJdbcTemplate.queryForList(sql2, parameters2);

Hextoraw() not working with IN clause while using NamedParameterJdbcTemplate

I am trying to update certain rows in my oracle DB using id which is of RAW(255).
Sample ids 0BF3957A016E4EBCB68809E6C2EA8B80, 1199B9F29F0A46F486C052669854C2F8...
#Autowired
private NamedParameterJdbcTemplate jdbcTempalte;
private static final String UPDATE_SUB_STATUS = "update SUBSCRIPTIONS set status = :status, modified_date = systimestamp where id in (:ids)";
public void saveSubscriptionsStatus(List<String> ids, String status) {
MapSqlParameterSource paramSource = new MapSqlParameterSource();
List<String> idsHexToRaw = new ArrayList<>();
String temp = new String();
for (String id : ids) {
temp = "hextoraw('" + id + "')";
idsHexToRaw.add(temp);
}
paramSource.addValue("ids", idsHexToRaw);
paramSource.addValue("status", status);
jdbcTempalte.update(*UPDATE_SUB_STATUS*, paramSource);
}
This above block of code is executing without any error but the updates are not reflected to the db, while if I skip using hextoraw() and just pass the list of ids it works fine and also updates the data in table. see below code
public void saveSubscriptionsStatus(List<String> ids, String status) {
MapSqlParameterSource paramSource = new MapSqlParameterSource();]
paramSource.addValue("ids", ids);
paramSource.addValue("status", status);
jdbcTempalte.update(UPDATE_SUB_STATUS, paramSource);
}
this code works fine and updates the table, but since i am not using hextoraw() it scans the full table for updation which I don't want since i have created indexes. So using hextoraw() will use index for scanning the table but it is not updating the values which is kind of weird.
Got a solution myself by trying all the different combinations :
#Autowired
private NamedParameterJdbcTemplate jdbcTempalte;
public void saveSubscriptionsStatus(List<String> ids, String status) {
String UPDATE_SUB_STATUS = "update SUBSCRIPTIONS set status = :status, modified_date = systimestamp where id in (";
MapSqlParameterSource paramSource = new MapSqlParameterSource();
String subQuery = "";
for (int i = 0; i < ids.size(); i++) {
String temp = "id" + i;
paramSource.addValue(temp, ids.get(i));
subQuery = subQuery + "hextoraw(:" + temp + "), ";
}
subQuery = subQuery.substring(0, subQuery.length() - 2);
UPDATE_SUB_STATUS = UPDATE_SUB_STATUS + subQuery + ")";
paramSource.addValue("status", status);
jdbcTempalte.update(UPDATE_SUB_STATUS, paramSource);
}
What this do is create a query with all the ids to hextoraw as id0, id1, id2...... and also added this values in the MapSqlParameterSource instance and then this worked fine and it also used the index for updating my table.
After running my new function the query look like : update
SUBSCRIPTIONS set status = :status, modified_date = systimestamp
where id in (hextoraw(:id0), hextoraw(:id1), hextoraw(:id2)...)
MapSqlParameterSource instance looks like : {("id0", "randomUUID"),
("id1", "randomUUID"), ("id2", "randomUUID").....}
Instead of doing string manipulation, Convert the list to List of ByteArray
List<byte[]> productGuidByteList = stringList.stream().map(item -> GuidHelper.asBytes(item)).collect(Collectors.toList());
parameters.addValue("productGuidSearch", productGuidByteList);
public static byte[] asBytes(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}

Resources