How to query data inserting substring - MYSQL/JPQL - java

I'm writing the below method on a Java project with Hibernate connecting to the database, and want to know how can I write a query to find a register with just the corrensponding substring. I tried using 'concat' but it didn't work
public List<UnidadeDeSaude> pesquisar(String nome) {
TypedQuery<UnidadeDeSaude> query = manager.createQuery("from UnidadeDeSaude where nomeDoEstabelecimento concat('%',:nomeDoEstabelecimento,'%')", UnidadeDeSaude.class);
query.setParameter("nomeDoEstabelecimento", nome + '%');
return query.getResultList();
}

What you're looking for is LIKE clause
from UnidadeDeSaude where nomeDoEstabelecimento like concat('%',:nomeDoEstabelecimento,'%')", UnidadeDeSaude.class);
And skip the + '%' from setParameter

Related

Get Entity ID after executing Native insert query + Spring JPA

I am using a Native query in my JPA Repository to run INSERT query - because, I couldn't run a few queries through JPA.
String INSERT_USER_IN_TO_DATABASE = "INSERT INTO USER_MASTER " +
"(" +
"MOBILE_X,USER_TYPE,COUNTRYCODE,USER_N,USER_LAST_N,GENDER,DOB) " +
"VALUES ( " +
"EncryptByKey(Key_GUID(convert(varchar,?1)), ?2)," +
"1,+91,?3,?4,?5,?6" +
")";
#Query(value = INSERT_USER_IN_TO_DATABASE, nativeQuery = true)
#Modifying
UserMasterEntity saveNewUser(String encryptionKey,
String phoneNumber,
String name,
String lastName,
String gender,
String dob);
My intention is to execute the INSERT statement and get me the userMaster entity. But I get the below exception
Caused by: java.lang.IllegalArgumentException: Modifying queries can only use void or int/Integer as return type! Offending method: public abstract com.officeride.fileprocess.job.bulkuser.UserMasterEntity com.officeride.fileprocess.job.bulkuser.UserMasterRepository.saveNewUser(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String)
I used #ColumnTransformer stuff too in my entity and nothing is working out for me.
How to tackle this?
You cannot make the INSERT INTO statement return anything. Depending on your database's support, on that end you could execute multiple statements, e.g., INSERT INTO ...; SELECT ..., but Spring Data JDBC does not support this.
What you can do is implement saveNewUser and perform the insert then select, sequentially and synchronously. See this: https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#repositories.single-repository-behavior.
If your database supports it, you could create a stored procedure that performs the insert then select.

Spring + MongoDB - MongoTemplate + Criteria Query

I am using Spring Boot + MongoDB. I need to query database based on some criteria where my methods looks like below:
#Override
public List<MyCollection> findBuyByCriteria(Request request) {
Query search = new Query();
search.addCriteria(Criteria.where("ItmId").in(request.getItmIds()));
return mongoTemplate.find(search, MyCollection.class);
}
Problem that I am facing is:
At line
search.addCriteria(Criteria.where("ItmId").in(request.getItmIds()));
request.getItmIds has 1 million Ids due to which I am getting an exception
org.bson.BsonMaximumSizeExceededException: Document size of 46282052 is larger than maximum of 16793600
Can anyone help me with this one?
If you are using Spring Data JPA, you can do something like:
findBySomeField(String someField)
If you have a more complex query, you can actually use JPQL and write a custom query.
#Query(value = "SELECT o.* from SomeObject o WHERE :someField IS NULL OR o.someField = :somefield)
public findBySomeField(#Param("someField") String someField);

How to use net.java.ao.Query to query issues?

I have this select in PostgreSQL:
SELECT "field_1", "field_2","field_3", MIN(COALESCE(NULLIF("field_4",'') ,'TBD')) MINDP,MIN("field_5") MINBOD FROM "MY_TABLE" GROUP BY "field_1", "field_2","field_3"
And I want to use net.java.ao.Query to query my database. My function:
import net.java.ao.Query;
public List<myClass> find() {
String SQL = "SELECT \"field_1\", \"field_2\",\"field_3\", MIN(COALESCE(NULLIF(\"field_4\",'') ,'TBD')) MINDP,MIN(\"field_5\") MINBOD FROM \"MY_TABLE\" GROUP BY \"field_1\", \"field_2\",\"field_3\""
return newArrayList(ao.find(myClass.class, Query.select(SQL)));
}
The problem is: this code return all issues of my table.
When I run this SQL in postgreSQL console it works fine.
My code has different results than SQL console.
Does anybody know why is this happening?
Old question but if somebody steps over this -
Query.select() is not meant to put plain SQL into. It is more of a builder to create 'database independant' query. See some examples here: Finding entities
public MyEntity[] findMyEntities(String fieldValue) {
final Query query = Query.select()
.where("FIELD_VALUE = ?", fieldValue);
// evaluates to something like this
// SELECT * FROM MY_ENTITY WHERE FIELD_VALUE = x;
return ao.find(MyEntity.class, query);
}
This is why there's another method (which you used in your comment), that can handle plain SQL (findWithSQL).
Attention Query and find() often do not work as expected, so you might not get your original query to work using the query builder.

Ebean query using setDistinct() does not work

I'm using an ebean query in the play! framework to find a list of records based on a distinct column. It seems like a pretty simple query but the problem is the ebean method setDistinct(true) isn't actually setting the query to distinct.
My query is:
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findList();
In my results I get duplicate artist names.
From what I've seen I believe this is the correct syntax but I could be wrong. I'd appreciate any help. Thank you.
I just faced the same issue out of the blue and can not figure it out. As hfs said its been fixed in a later version but if you are stuck for a while you can use
findSet()
So in your example use
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findSet();
According to issue #158: Add support for using setDistinct (by excluding id property from generated sql) on the Ebean bug tracker, the problem is that an ID column is added to the beginning of the select query implicitly. That makes the distinct keyword act on the ID column, which will always be distinct.
This is supposed to be fixed in Ebean 4.1.2.
As an alternative you can use a native SQL query (SqlQuery).
The mechanism is described here:
https://ebean-orm.github.io/apidocs/com/avaje/ebean/SqlQuery.html
This is from the documentation:
public interface SqlQuery
extends Serializable
Query object for performing native SQL queries that return SqlRow's.
Firstly note that you can use your own sql queries with entity beans by using the SqlSelect annotation. This should be your first approach when wanting to use your own SQL queries.
If ORM Mapping is too tight and constraining for your problem then SqlQuery could be a good approach.
The returned SqlRow objects are similar to a LinkedHashMap with some type conversion support added.
// its typically a good idea to use a named query
// and put the sql in the orm.xml instead of in your code
String sql = "select id, name from customer where name like :name and status_code = :status";
SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
sqlQuery.setParameter("name", "Acme%");
sqlQuery.setParameter("status", "ACTIVE");
// execute the query returning a List of MapBean objects
List<SqlRow> list = sqlQuery.findList();
i have a solution for it:-
RawSql rawSql = RawSqlBuilder
.parse("SELECT distinct CASE WHEN PARENT_EQUIPMENT_NUMBER IS NULL THEN EQUIPMENT_NUMBER ELSE PARENT_EQUIPMENT_NUMBER END AS PARENT_EQUIPMENT_NUMBER " +
"FROM TOOLS_DETAILS").create();
Query<ToolsDetail> query = Ebean.find(ToolsDetail.class);
ExpressionList<ToolsDetail> expressionList = query.setRawSql(rawSql).where();//ToolsDetail.find.where();
if (StringUtils.isNotBlank(sortBy)) {
if (StringUtils.isNotBlank(sortMode) && sortMode.equals("descending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
//expressionList.orderBy().asc(sortBy);
}else if (StringUtils.isNotBlank(sortMode) && sortMode.equals("ascending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"asc");
// expressionList.orderBy().asc(sortBy);
} else {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
}
}
if (StringUtils.isNotBlank(fullTextSearch)) {
fullTextSearch = fullTextSearch.replaceAll("\\*","%");
expressionList.disjunction()
.ilike("customerSerialNumber", fullTextSearch)
.ilike("organizationalReference", fullTextSearch)
.ilike("costCentre", fullTextSearch)
.ilike("inventoryKey", fullTextSearch)
.ilike("toolType", fullTextSearch);
}
//add filters for date range
String fromContractStartdate = Controller.request().getQueryString("fm_contract_start_date_from");
String toContractStartdate = Controller.request().getQueryString("fm_contract_start_date_to");
String fromContractEndtdate = Controller.request().getQueryString("fm_contract_end_date_from");
String toContractEnddate = Controller.request().getQueryString("fm_contract_end_date_to");
if(StringUtils.isNotBlank(fromContractStartdate) && StringUtils.isNotBlank(toContractStartdate))
{
Date fromSqlStartDate=new Date(AppUtils.convertStringToDate(fromContractStartdate).getTime());
Date toSqlStartDate=new Date(AppUtils.convertStringToDate(toContractStartdate).getTime());
expressionList.between("fmContractStartDate",fromSqlStartDate,toSqlStartDate);
}if(StringUtils.isNotBlank(fromContractEndtdate) && StringUtils.isNotBlank(toContractEnddate))
{
Date fromSqlEndDate=new Date(AppUtils.convertStringToDate(fromContractEndtdate).getTime());
Date toSqlEndDate=new Date(AppUtils.convertStringToDate(toContractEnddate).getTime());
expressionList.between("fmContractEndDate",fromSqlEndDate,toSqlEndDate);
}
PagedList pagedList = ToolsQueryFilter.getFilter().applyFilters(expressionList).findPagedList(pageNo-1, pageSize);
ToolsListCount toolsListCount = new ToolsListCount();
toolsListCount.setList(pagedList.getList());
toolsListCount.setCount(pagedList.getTotalRowCount());
return toolsListCount;

How to prevent SQL Injection with JPA and Hibernate?

I am developing an application using hibernate. When I try to create a Login page, The problem of Sql Injection arises.
I have the following code:
#Component
#Transactional(propagation = Propagation.SUPPORTS)
public class LoginInfoDAOImpl implements LoginInfoDAO{
#Autowired
private SessionFactory sessionFactory;
#Override
public LoginInfo getLoginInfo(String userName,String password){
List<LoginInfo> loginList = sessionFactory.getCurrentSession().createQuery("from LoginInfo where userName='"+userName+"' and password='"+password+"'").list();
if(loginList!=null )
return loginList.get(0);
else return null;
}
}
How will i prevent Sql Injection in this scenario ?The create table syntax of loginInfo table is as follows:
create table login_info
(user_name varchar(16) not null primary key,
pass_word varchar(16) not null);
Query q = sessionFactory.getCurrentSession().createQuery("from LoginInfo where userName = :name");
q.setParameter("name", userName);
List<LoginInfo> loginList = q.list();
You have other options too, see this nice article from mkyong.
You need to use named parameters to avoid sql injection. Also (nothing to do with sql injection but with security in general) do not return the first result but use getSingleResult so if there are more than one results for some reason, the query will fail with NonUniqueResultException and login will not be succesful
Query query= sessionFactory.getCurrentSession().createQuery("from LoginInfo where userName=:userName and password= :password");
query.setParameter("username", userName);
query.setParameter("password", password);
LoginInfo loginList = (LoginInfo)query.getSingleResult();
What is SQL Injection?
SQL Injection happens when a rogue attacker can manipulate the query
building process so that he can execute a different SQL statement than
what the application developer has originally intended
How to prevent the SQL injection attack
The solution is very simple and straight-forward. You just have to make sure that you always use bind parameters:
public PostComment getPostCommentByReview(String review) {
return doInJPA(entityManager -> {
return entityManager.createQuery("""
select p
from PostComment p
where p.review = :review
""", PostComment.class)
.setParameter("review", review)
.getSingleResult();
});
}
Now, if some is trying to hack this query:
getPostCommentByReview("1 AND 1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) )");
the SQL Injection attack will be prevented:
Time:1, Query:["select postcommen0_.id as id1_1_, postcommen0_.post_id as post_id3_1_, postcommen0_.review as review2_1_ from post_comment postcommen0_ where postcommen0_.review=?"], Params:[(1 AND 1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) ))]
JPQL Injection
SQL Injection can also happen when using JPQL or HQL queries, as demonstrated by the following example:
public List<Post> getPostsByTitle(String title) {
return doInJPA(entityManager -> {
return entityManager.createQuery(
"select p " +
"from Post p " +
"where" +
" p.title = '" + title + "'", Post.class)
.getResultList();
});
}
The JPQL query above does not use bind parameters, so it’s vulnerable to SQL injection.
Check out what happens when I execute this JPQL query like this:
List<Post> posts = getPostsByTitle(
"High-Performance Java Persistence' and " +
"FUNCTION('1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) ) --',) is '"
);
Hibernate executes the following SQL query:
Time:10003, QuerySize:1, BatchSize:0, Query:["select p.id as id1_0_, p.title as title2_0_ from post p where p.title='High-Performance Java Persistence' and 1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) ) --()=''"], Params:[()]
Dynamic queries
You should avoid queries that use String concatenation to build the query dynamically:
String hql = " select e.id as id,function('getActiveUser') as name from " + domainClass.getName() + " e ";
Query query=session.createQuery(hql);
return query.list();
If you want to use dynamic queries, you need to use Criteria API instead:
Class<Post> entityClass = Post.class;
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = cb.createTupleQuery();
Root<?> root = query.from(entityClass);
query.select(
cb.tuple(
root.get("id"),
cb.function("now", Date.class)
)
);
return entityManager.createQuery(query).getResultList();
I would like to add here that is a peculiar SQL Injection that is possible with the use of Like queries in searches.
Let us say we have a query string as follows:
queryString = queryString + " and c.name like :name";
While setting the name parameter, most would generally use this.
query.setParameter("name", "%" + name + "%");
Now, as mentioned above traditional parameter like "1=1" cannot be injected because of the TypedQuery and Hibernate will handle it by default.
But there is peculiar SQL Injection possible here which is because of the LIKE Query Structure which is the use of underscores
The underscore wildcard is used to match exactly one character in
MySQL meaning, for example, select * from users where user like
'abc_de'; This will produce outputs as users that start with abc, end
with de and have exactly 1 character in between.
Now, if in our scenario, if we set
name="_" produces customers whose name is at least 1 letter
name="__" produces customers whose name is at least 2 letters
name="___" produces customers whose name is at least 3 letters
and so on.
Ideal fix:
To mitigate this, we need to escape all underscores with a prefix .
___ will become \_\_\_ (equivalent to 3 raw underscores)
Likewise, the vice-versa query will also result in an injection in which %'s need to be escaped.
We should always try to use stored Procedures in general to prevent SQLInjection.. If stored procedures are not possible; we should try for Prepared Statements.

Categories