Check if row record exists with JPA - java

I want to check if row record by id exists. I tried this:
public Optional<PaymentTransactions> transactionByUnique_Id( String unique_id ) throws Exception
{
String hql = "select e from " + PaymentTransactions.class.getName() + " e where e.unique_id = :unique_id";
TypedQuery<PaymentTransactions> query = entityManager.createQuery( hql, PaymentTransactions.class ) .setParameter( "unique_id", unique_id );
Optional<PaymentTransactions> paymentTransaction = query.getResultList().stream().findFirst();
return paymentTransaction;
}
Optional<PaymentTransactions> tnx = transactionsService.transactionByUnique_Id( transaction.getTransactionId() );
if(tnx.isPresent())
{
return "test";
// This method should be called only when DB record exists
}
But I alway get present object. What is the proper way to implement this check.

Related

Query with 3 tables in native sql with hibernate

I have 3 tables related with their FK and I trying to do a native query selecting entities joined. I did with 2 tables joined and it worked, but it does not work with 3.
This is my generic method for it
public List<Object[]> select3Tables(String sql, String parameter,
String tableAlias, String entityName, String tableAlias2, String path,
String tableAlias3, String path2){
Transaction tx = null;
List<Object[]>returnList = null;
try {
Query query = session.createNativeQuery(sql)
.addEntity(tableAlias, entityName)
.addJoin(tableAlias2, path)
.addJoin(tableAlias3, path2)
.setParameter("parameter", parameter);
returnList = query.getResultList();
tx.commit();
} catch (HibernateException e) {
if(tx!=null) tx.rollback();
System.err.println(e.getMessage());
e.printStackTrace();
}
and here is where I triying to do it.
public List<Compra> selectCompraByPropietario(final String dniPropitario) {
Compra compra = new Compra();
String sql = "SELECT * FROM Compras c "
+ "JOIN Montes m ON c.id_compra = m.compraFK_id_compra "
+ "JOIN Propietarios p ON m.propietarioFK_id_propietario = p.id_propietario"
+ "WHERE p.dni =:parameter";
List<Object[]> list = new HibernateSession().select3Tables(sql, dniPropitario, "compras",
"backend.entities.Compra", "m", "compras.montes", "m", "propietarios.montes");
for(Object[]listData : list) {
compra = (Compra) listData[0];
data.add(compra);
}
System.out.println("tamaño de data " + data.size());
return data;
}
Currently I´m getting an error "could not determine fetch owner : null"
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.HibernateException: Could not determine fetch owner : null
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1626)
at org.hibernate.query.Query.getResultList(Query.java:165)
at backend.HibernateSession.select3Tables(HibernateSession.java:149)
at backend.repositories.CompraOperations.selectCompraByPropietario(CompraOperations.java:90)
at backend.MainBackend.main(MainBackend.java:54)
Caused by: org.hibernate.HibernateException: Could not determine fetch owner : null
at org.hibernate.loader.custom.CustomLoader.determineAppropriateOwnerPersister(CustomLoader.java:292)
any idea what am I doing wrong and how to make it work?
Thank you.
I have faced quite similar problems like you
Everything seems fine to me.
Just add a space after
+ "JOIN Propietarios p ON m.propietarioFK_id_propietario = p.id_propietario"
Write it as
+ "JOIN Propietarios p ON m.propietarioFK_id_propietario = p.id_propietario "
Hope this helps

Is there a way to check for null ZonedDateTime in JPQL?

In some E2E tests I'm faced with a problem. Let's say, I have the following JPQL query:
Query query = entityManager.createQuery(
" select d from Document d left join d.audit da " +
" where " +
" (cast(:startDate as java.time.ZonedDateTime)) is null " +
" or truncate_for_minutes(da.dateCreate, 'UTC') >= " +
" truncate_for_minutes(:startDate, 'UTC')")
.setParameter("startDate", ZonedDateTime.now());
In the query string I use named parameter startDate which can be null. The query above works. But if I pass null, the following exception is thrown:
Caused by: org.postgresql.util.PSQLException:
ERROR: cannot cast type bytea to timestamp without time zone
Without type casting the following exception is thrown:
Caused by: org.postgresql.util.PSQLException:
ERROR: could not determine data type of parameter $1
Without check for null the following exception is thrown:
Caused by: org.postgresql.util.PSQLException:
ERROR: function pg_catalog.timezone(unknown, bytea) does not exist
No function matches the given name and argument types.
You might need to add explicit type casts.
I use this query in Spring Data repository by using #Query string. Function truncate_for_minute(...) - is just a small customization for the PostgreSQL function date_trunc(...).
I know that I can implement custom repository and build query string dynamically, but why I can't check for null ZonedDateTime in JPQL string? Maybe there is a way to do it?
My environment:
Java 11
PostgreSQL 11.3 on x86_64-pc-linux-musl, compiled by gcc (Alpine 8.3.0) 8.3.0, 64-bit
Hibernate 5.3.7 Final
Another solution is to use Criteria API and build the query dynamically.
#Repository
#RequiredArgsConstructor
public class CustomDocumentRepositoryImpl implements CustomDocumentRegistry {
private final EntityManager em;
#Override
public Page<Document> findDocumentsForExpertByFilter(SearchDocumentCriteria criteria,
Pageable pageable) {
final String AUDIT_TABLE = "...";
final String USER_TABLE = "...";
final String ID_FIELD = "id";
final String FIRST_NAME_FIELD = "...";
final String LAST_NAME_FIELD = "...";
final String MIDDLE_NAME_FIELD = "...";
final String WORKSTATION_FIELD = "...";
final String DATE_CREATE_FIELD = "...";
final String LIKE_MASK = "%%%s%%";
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Document> query = cb.createQuery(Document.class);
Root<Document> root = query.from(Document.class);
Path<ZonedDateTime> dateCreatePath =
root.get(AUDIT_TABLE).get(DATE_CREATE_FIELD);
Path<String> lastNamePath =
root.get(AUDIT_TABLE).get(USER_TABLE).get(LAST_NAME_FIELD);
Path<String> firstNamePath =
root.get(AUDIT_TABLE).get(USER_TABLE).get(FIRST_NAME_FIELD);
Path<String> middleNamePath =
root.get(AUDIT_TABLE).get(USER_TABLE).get(MIDDLE_NAME_FIELD);
root.fetch(AUDIT_TABLE, JoinType.LEFT)
.fetch(USER_TABLE, JoinType.LEFT);
Predicate documentIdsPredicate;
List<Long> documentIds = criteria.getIds();
if (isNull(documentIds) || documentIds.isEmpty()) {
documentIdsPredicate = cb.isNotNull(root.get(ID_FIELD));
} else {
documentIdsPredicate = root.get(ID_FIELD).in(criteria.getIds());
}
Predicate startDatePredicate;
ZonedDateTime startDate = criteria.getStartDate();
if (isNull(startDate)) {
startDatePredicate = cb.isNotNull(dateCreatePath);
} else {
startDatePredicate = cb.greaterThanOrEqualTo(dateCreatePath, startDate);
}
Predicate endDatePredicate;
ZonedDateTime endDate = criteria.getEndDate();
if (isNull(endDate)) {
endDatePredicate = cb.isNotNull(dateCreatePath);
} else {
endDatePredicate = cb.lessThanOrEqualTo(dateCreatePath, endDate);
}
Predicate lastNamePredicate = cb.like(cb.upper(lastNamePath),
format(LIKE_MASK, criteria.getLastName().toUpperCase()));
Predicate firstNamePredicate = cb.like(cb.upper(firstNamePath),
format(LIKE_MASK, criteria.getFirstName().toUpperCase()));
Predicate middleNamePredicate = cb.like(cb.upper(middleNamePath),
format(LIKE_MASK, criteria.getMiddleName().toUpperCase()));
Predicate fullNamePredicate =
cb.and(lastNamePredicate, firstNamePredicate, middleNamePredicate);
Predicate compositePredicate = cb.and(
fullNamePredicate,
documentIdsPredicate,
startDatePredicate,
endDatePredicate
);
query.where(compositePredicate);
Query limitedQuery = em.createQuery(query
.orderBy(cb.desc(root.get(AUDIT_TABLE).get(DATE_CREATE_FIELD))))
.setFirstResult(nonNull(criteria.getSize()) ?
criteria.getPage() * criteria.getSize() :
criteria.getPage());
if (nonNull(criteria.getSize())) {
limitedQuery.setMaxResults(criteria.getSize());
}
List<Document> documents = limitedQuery.getResultList();
return new PageImpl<>(documents, pageable, criteria.getSize());
}
}
Generates the following SQL:
select
document0_.id as id1_3_0_,
user1_.id as id1_13_1_,
document0_.created_dt as created_2_3_0_,
document0_.updated_dt as updated_3_3_0_,
document0_.created_user_id as created_6_3_0_,
document0_.updated_user_id as updated_7_3_0_,
document0_.name as name4_3_0_,
user1_.first_name as first_na2_13_1_,
user1_.last_name as last_nam3_13_1_,
user1_.middle_name as middle_n5_13_1_
from
some_scheme.document document0_
left outer join
some_scheme.s_user user1_
on document0_.created_user_id=user1_.id cross
join
some_scheme.s_user user2_
where
document0_.created_user_id=user2_.id
and (
upper(user2_.last_name) like '%LASTNAME%'
)
and (
upper(user2_.first_name) like '%FIRSTNAME%'
)
and (
upper(user2_.middle_name) like '%MIDDLENAME%'
)
and (
document0_.id in (
2 , 1
)
)
and document0_.created_dt>=...
and document0_.created_dt<=...
order by
document0_.created_dt desc limit 10;
In my case, the problem was as follows. I registered customization of SQL function date_trunc:
public class CustomSqlFunction implements MetadataBuilderContributor {
#Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"truncate_for_minutes",
new SQLFunctionTemplate(
StandardBasicTypes.TIMESTAMP,
"date_trunc('minute', (?1 AT TIME ZONE ?2))"
)
);
}
}
If change the StandardBasicTypes.TIMESTAMP to ZonedDateTime.INSTANCE and pass ZonedDateTime in one parameter, then the comparison in the JPQL query does not cause errors:
public class CustomSqlFunction implements MetadataBuilderContributor {
#Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"truncate_for_minutes",
new SQLFunctionTemplate(
ZonedDateTimeType.INSTANCE,
"date_trunc('minute', ?1)"
)
);
}
}

Is it possible to return data set instead of any mapped model class in MyBatis?

I have scenario where I want to make dynamic query which myBatis supposed to support like as below :
<select id=“someRecords” resultMap=“someRecordMap”>
DROP TABLE IF EXISTS TEMP_TABLE;
CREATE TEMPORARY TABLE TEMP_TABLE(some_stub UUID);
INSERT INTO TEMP_TABLE (some_stub)
select regexp_split_to_table(#{someIds},',')::uuid;
SELECT wil.some_identifier_stub as identifier_stub
,wil.x
,wil.y
,wil.z
,wil.u
,wil.o
,msg.p
FROM TABLE_A msg
INNER JOIN TABLE_B wil ON msg.a_id = wil.b_id
INNER JOIN TABLE_C est ON est.c_stub = wil.b_stub
WHERE wil.unique_id = #{uniqueId} AND wil.b_type_id = #{b_TypeId}
<if test="environment != null">
<include refid="environmentCondition"></include>
</if>
</select>
<sql id="environmentCondition">
AND environment = #{environment}
</sql>
But instead of someRecordMap, I want to return DataSet so that it become backward compatible with my existing code
So instead of suing myBatis XMl approach I just make custom approach using reflection and annotations like below :
Section : Dynamic Query Based on some condition like IGNORE_SOME_JOIN, IGNORE_SOME_STUB, IGNORE_SOME_EXT_FLAG
#SqlQueries({#SqlQuery(name = "query1",
query = "select a,b," +
"c,d,e,f,g,wil.h," +
" j,h,i " +
START_DELIMITER + " " + IGNORE_SOME_JOIN + " " +
" ,some_message" + END_DELIMITER +
" FROM A_TABLE wil " +
START_DELIMITER + " " + IGNORE_SOME_JOIN + " " +
"LEFT OUTER JOIN B_TABLE wim on" +
" wil.unique_id = wim.unique_id" +
" and wim.created_date >= ? and wim.created_date <= ? " + END_DELIMITER +
" WHERE ( wil.created_date >= ? AND wil.created_date <= ? AND wil.primary_id = ? " +
START_DELIMITER + " " + IGNORE_SOME_STUB + " " +
" AND wil.unique_identifier_stub = ?::uuid " + END_DELIMITER +
START_DELIMITER + " " + IGNORE_SOME_EXT_FLAG +
" AND wil.some_ext_success_flag = ANY(?) " + END_DELIMITER + ")" +
"ORDER BY wil.created_date OFFSET ? LIMIT ? ")}
)
parsing logic for dynamic query be like :
abstract class ReportingQuery {
private static final Logger LOG = LoggerFactory.getLogger(ReportingQuery.class);
static final String START_DELIMITER = "#~";
static final String END_DELIMITER = "#~";
static final String REPLACEABLE_DELIMITER = "--";
/**
* Responsible to prepare final query after applying dynamic query criteria
*
* #param className : ReportingQuery class reference
* #param methodName : Query method name
* #param queryName : Dynamic query
* #param ignoreStrings : Criteria to be applied in dynamic query
* #return : final static query after applying dynamic query criteria(ignoreStrings)
*/
static Optional<String> getQuery(Class className, String methodName, String queryName, List<String> ignoreStrings) {
StringBuilder builder = new StringBuilder();
try {
Method[] methods = className.getDeclaredMethods();
Optional<String> queryString = Optional.empty();
if (Arrays.stream(methods).anyMatch(x -> x.getName().equals(methodName))) {
QueryExample.SqlQuery[] sqlQueries = Arrays.stream(methods)
.filter(x -> x.getName().equals(methodName))
.findFirst().get().getAnnotation(ReportingQuery.SqlQueries.class).value();
if (Arrays.stream(sqlQueries).anyMatch(x -> x.name().equals(queryName))) {
queryString = Optional.of(Arrays.stream(sqlQueries).filter(x -> x.name()
.equals(queryName)).findFirst().get().query());
}
}
String[] token = new String[0];
if (queryString.isPresent()) {
token = queryString.get().split(START_DELIMITER);
}
...... SOME logic to make query based on some dynamic condition
return Optional.of(builder.toString());
}
/**
*
*/
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#interface SqlQuery {
String name();
String query();
}
/**
*
*/
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#interface SqlQueries {
SqlQuery[] value();
}
}
So if I have condition IGNORE_SOME_JOIN then my final query with logic would be like
select a,b,c,d,e,f,g,wil.h,j,h,i FROM A_TABLE wil WHERE ( wil.created_date >= '2018-08-29T15:15:42.42'
AND wil.created_date <= '2018-08-30T15:15:42.42' AND wil.acct_id = 2000017
AND wil.unique_identifier_stub = 'a004f322-1003-40a7-a54b-f3b979744fd2'
AND wil.some_ext_success_flag = ANY('{"0","1"}')) ORDER BY wil.created_date OFFSET 0 LIMIT 500;
So with above I got query as string and I'll run below code and get Result Set :
PreparedStatement ps = con.prepareStatement(query)) {
prepareStatement(prepareStatementAndQueryList, ps, con);
try (ResultSet rs = ps.executeQuery()) {
DO SOMETHING WITH RESULT SET NOW
}
}
But I want to do this with MyBatis instead my own custom solution which would little error prone and might be not performance efficient as I have used much reflection here.
Yes, this is possible and is directly supported by mybatis. Here is an example from the documentation:
#SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(String name);
class UserSqlBuilder {
public static String buildGetUsersByName(final String name) {
return new SQL(){{
SELECT("*");
FROM("users");
if (name != null) {
WHERE("name like #{value} || '%'");
}
ORDER_BY("id");
}}.toString();
}
}
In the example above getUsersByName is a mapper method and UserSqlBuilder is used by the mapper to generate SQL query text dynamically based on mapper parameters. It's exactly what you need and very similar to what ReportingQuery does.
You need to adapt your code so that your existing query generator confirms to SelectProvider API but this seems to be rather straightforward.

Update a column using hibernate

I have two classes Employee and Department.
public class Employee {
int empId;
String empName;
Boolean isEmpAvailable;
String empAddress;
Department department;
}
public class Department {
int deptId;
String deptName;
}
I have created hibernate files files for both classes Department.hbm.xml and Employee.hbm.xml
I like to update the column isEmpAvailable in the table Employee basing on a deptid in Department table.
Here I am facing problem with update query which I am not clear after reading in online documentation
public void updateEmployee(Employee emp, Department deptid){
String query= " update Employee set isEmpAvailable=? where deptid=?
Object[] values= {"true","133"};
getHibernateTemplate.update(query,values);
}
When i run the code the column doesn't get update. A error is thrown as
Entity not recognized: update Employee set isEmpAvailable=? where deptid=?
I have read online docs which have methods of getHibernateTemplate() which have return type as integer. Here I like to update the database directy by calling dao.updateEmployee without any returntype. I am unable do it. Please suggest me
Update in hibernate is done this way :
String hqlUpdate =
"update Employee e " +
"set e.isEmpAvailable = :isEmpAvailable " +
"where e.deptid = :deptid";
int updatedEntities = session.createQuery( hqlUpdate )
.setBoolean( "isEmpAvailable", isEmpAvailable )
.setInt( "deptid", deptid )
.executeUpdate();
OR
String jpqlUpdate =
"update Employee e " +
"set e.isEmpAvailable = :isEmpAvailable " +
"where e.deptid = :deptid";
int updatedEntities = entityManager.createQuery( jpqlUpdate )
.setBoolean( "isEmpAvailable", isEmpAvailable )
.setInt( "deptid", deptid )
.executeUpdate();
OR
String hqlVersionedUpdate =
"update versioned Employee e " +
"set e.isEmpAvailable = :isEmpAvailable " +
"where e.deptid = :deptid";
int updatedEntities = s.createQuery( hqlUpdate )
.setBoolean( "isEmpAvailable", isEmpAvailable )
.setInt( "deptid", deptid )
.executeUpdate();
If you want you can also use the saveOrUpdate() function. In this link there is an example and some documentation.

Translate Native Query to Hibernate Query

I have following method to extract data from DB table
public User getNextUser() {
final EntityManager em = getEntityManagerFactory().createEntityManager();
final String sql = "UPDATE user " +
"SET processing = TRUE " +
"WHERE id = (" +
"SELECT id " +
"FROM user " +
"WHERE processing = FALSE " +
"LIMIT 1 FOR UPDATE) " +
"RETURNING *";
final Query query = em.createNativeQuery(sql, User.class);
User result = null;
try {
List userList = query.getResultList();
if (!userList.isEmpty()) {
result = (User) userList.get(0);
}
} finally {
em.close();
}
return result;
}
How it's possible to convert this native query to Hibernate query (to not invalidate 2nd level cache)? I have read that equivalent of
SELECT ... FOR UPDATE
in Hibernate it's possible to implement via
PESSIMISTIC_WRITE

Categories