JDBCTemplate Boolean not mapping properly with BeanPropertyRowMapper - java

I have a standard MySql DB with a User Table. In this table there is a TINYINT(4) named "isApproved"
For this user it is set to "1" (which i believe is true) My bean property looks like this:
private boolean isApproved;
and the appropriate getters/setters:
public boolean isApproved() {
return isApproved;
}
public void setApproved(boolean isApproved) {
this.isApproved = isApproved;
}
I have a jdbcTemplate that is pulling the correct user and it looks like this:
public UserBean findUserByUserName(String userName) {
String sql = "SELECT * FROM User WHERE name = ?";
return (UserBean)getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper<UserBean>(UserBean.class), userName);
}
So I think I may have made a simple mistake and I add this to the logging:
logger.debug("User from DB isApproved:"+userFromDb.isApproved());
This is coming back "false", even when there is a "1" in the DB.
How do I make this mapping correct, the isApproved is "1" which should equate to true as a boolean.

Bean naming mean that you take the column name, then call the setter method setColumnName, and the getter method getColumnName, except for boolean where it is named isColumnName.
Your column is named isApproved, so standard bean name would be setIsApproved and isIsApproved. See the problem?
As the javadoc of BeanPropertyRowMapper says:
To facilitate mapping between columns and fields that don't have matching names, try using column aliases in the SQL statement like:
select fname as first_name from customer
This means you can't use SELECT *, but that's ok, because you should never use SELECT * in code. See Why is SELECT * considered harmful?
The * is convenience for ad-hoc queries, or queries for dynamic data where you use the meta-data to analyze the result columns. Since you need to map to a specific class, you need to select the columns that need to map.

Related

Stored procedure call with Spring JPA

I need to invoke a stored procedure using the JPA. The stored procedure operates on multiple tables and return some of the columns from these tables.
Tried with the #Procedure it doesn't seem to work, always the stored procedure is not found in this case.
Directly calling the procedure using native query was successful, but with this approach, I am need to map the result returned to List of an Object.
My implementation in the repository looks like below,
#Query(value = "EXECUTE dbs.multitable_Test :inputObj", nativeQuery = true)
List<sp> multitable_Test(#Param("inputObj")String inputObj);
The result returned from the stored procedure needs to be mapped to the sp class.
How can this be achieved while we have multiple tables response in the single result set?
Already tried with the attributeConvert from
this link, still getting the below exception.
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type
Any help with this is appreciated.
Firstly, this is not really the use case for procedure. Procedure is meant to modify data on the database without any return value, then you could use:
#Procedure(procedureName = "procedure_name")
void procedure(); // notice void
You should rather use a function using create function syntax. Function can modify data and return the result.
Secondly if you want to map it to some class, I see two solutions (using EntityManager):
Using ResultTransformer:
entityManager.createNativeQuery(
"select * from function_name(:parameter)"
)
.setParameter("parameter", parameter)
.unwrap(org.hibernate.query.NativeQuery.class)
.setResultTransformer(new ResultTransformer() {
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
return new Sp(tuple[0]);
}
#Override
public List transformList(List collection) {
return collection;
}
})
.getResultList();
Note that ResultTransformer is deprecated, but is so powerful, it will not be removed until there is a sensible replacement, see the note from hibernate developer.
Using ResultSetMapping. Place the proper annotation over an entity:
#SqlResultSetMapping(
name = "sp_mapping",
classes = #ConstructorResult(
targetClass = Sp.class,
columns = {
#ColumnResult(name = "attribute", type = Long.class)
})
)
And invoke the function using the mapping as parameter:
entityManager.createNativeQuery(
"select * " +
"from function_name(:parameter);",
"sp_mapping"
)
.setParameter("parameter", parameter)
.getResultList();

Getting empty result from the #Query in Hibernate

I am using Hibernate to execute my query, in admin panel i am getting correct result but while using in Hibernate it is not giving any result.
Dao layer -
#Query("select new com.eventila.pms.entity.ReferenceLead(projectId,count(lm)) from LeadMaster lm where lm.vendorId= ?1 and lm.source = 'share' group by lm.projectId")
List<ReferenceLead> getReferenceByUser(String userId);
Pojo -
#lombok.Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ReferenceLead {
String projectId;
Long referenceLead;
Long count;
protected ReferenceLead(){}
public ReferenceLead(String projectId,Long count) {
this.projectId=projectId;
this.count=count;
}
}
After executing this i am getting a empty list.
Please help me out.
In your select query return the fields without calling new constructor:
#Query("select projectId, count(lm) from LeadMaster lm where lm.vendorId = ?1 and lm.source = 'share' group by lm.projectId")
List<ReferenceLead> getReferenceByUser(String userId);
Hibernate will instantiate the object using these fields. Also, add #Entity annotation to your ReferenceLead class.
'source' is the keyword in SQL.
It is a keyword used in MERGE. i.e. WHEN NOT MATCHED BY SOURCE.
The word MATCHED also exhibits the same behaviour in that it gets highlighted grey in the editor.
Neither of these are reserved keywords though so if used as an identifier they do not need to be delimited (unless you find the syntax highlighting distracting).

SQL injection vulnerability with order by clause in Java

Veracode report is showing a SQL injection flaw for the below query. There are few parameters which I need to get the property file and then need to inject into my SQL, i.e. schema name, sorting order, etc.
I tried to use the %s with String.format, but veraCode is still showing it as a flaw. For the parameter it is fine, since I used map, but for schema and sorting order, it's still showing a flaw.
Any approach to solve this vulnerability?
phoneLogsQuery = "(select * from %s.SHORETEL_EVENTS_CALL_LOGS where CONVERT( date, CallDateTime,112 ) > CONVERT( date, GETDATE()-%s,112) "
+ " and (CALLER_CONTACT_ID in (:contactId) or CALLED_CONTACT_ID in (:contactId)) and EXTERNAL_CALL = 1 "
+ "UNION "
+ "select * from %s.SHORETEL_EVENTS_CALL_LOGS where CONVERT( date, CallDateTime,112 ) > CONVERT( date, GETDATE()-%s,112) "
+ " and (CALLER_CONTACT_ID in (:contactId) or CALLED_CONTACT_ID in (:contactId))"
+ " and GUILOGIN_NAME = :guiloginName and EXTERNAL_CALL = 0)"
+ " order by CallDateTime %s %s ";
phoneLogsQuery = String.format(phoneLogsQuery, schemaname, phoneLogAllData, schemaname, phoneLogAllData, sortDir, offsetQuery);
shoretelPhoneLogRow = jdbcTemplate.query(phoneLogsQuery,params,
new ShoretelPhoneLogMapper());
For column values you should use prepared statement. It makes injection impossible.Example :
jdbcTemplate.query("Select * from user where id=?"), new PreparedStatementSetter() {
public void setValues(PreparedStatement preparedStatement) throws SQLException
{
preparedStatement.setLong(1, id);
}
}, new ResultSetExtractor<User>() {
...
}});
Unfortunately, for column names, schema names, etc., you can use only concatenation or replace method:
"SELECT quantity from $tableName where event_id=?".replace("$tableName", "myTable")
The main thing that you should understand is that injection comes from end users and if those schema name, table name, etc., are inner info enduser can't change it. So you shouldn't be afraid of SQL injection.
Most of those values sadly cannot be interpolated by prepared statements, which means that you're going to have to do your own escaping. Not sure if Veracode is smart enough to detect it, but I'd go with StringEscapeUtils.escapeSql from apache commons lang
Use enum, it will resolve the issue. Enum limits the values you can pass to that parameter. Hence it will allow only given values. Even most of the security detection tools also consider this and will not report this as issue. If you do not want to expose column name, have different values in enum with constructore.
public class RequestDTO {
private SortOrder order;
private SortColumn colum;
}
public enum SortOrder {
asc,
desc
}
public enum SortColumn {
empployee_name
employee_department,
employee_salary
}
Basic premise of fixing SQL injections is to not use your request object directly but by first sanitizing, validating and creating new objects / references (if needed), then fetching values from that sanitized object.
So, create a method to sanitize, validate & get values that you wish to append for your order by clause and use that value instead of directly from request.
An append with StringBuilder will be OK provided you don't directly take values from non-sanitized request.
In my opinion, you seem to getting your %s from request directly and that is why Veracode is complaining.

Using a NamedParamterJdbcTemplate object to specify a mysql field and value to query on

I am currently in the process of learning the Java Spring Framework, and I am having difficulty understanding why the following query is failing to return any results from the database.
I am ultimately trying to create a where method in my OffersDAO class that allows my to query on a specific field, for a specific value.
public List<Offer> where(String field, String value){
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("field", field);
params.addValue("value", value);
String sql = "select * from offers where :field = :value";
return jdbc.query(sql, params, new RowMapper<Offer>(){
public Offer mapRow(ResultSet rs, int arg1) throws SQLException {
Offer offer = new Offer();
offer.setId(rs.getInt("id"));
offer.setName(rs.getString("name"));
offer.setText(rs.getString("text"));
offer.setEmail(rs.getString("email"));
return offer;
}
});
}
I am able to successfully query the database for results when I specify the field explicitly, as follows:
String sql = "select * from offers where name = :value";
Obviously there is something wrong with specifying the field name dynamically. My guess is it is most likely due to the fact that the field key is being inserted as a mysql string (with ''), when in fact mysql expects a column name for the :field placeholder.
My questions are as follows:
Is there a way to accomplish what I am attempting to do above, using the jdbc NamedParameterJdbcTemplate class?
If I cannot accomplish the above, by what means can I?
Thank you
Edit: No exceptions are thrown. In the case when I am attempting to supply the column name, a empty result set is returned.
You can't specify the field name in a parameter - only the field value. Since you know the DB schema when you're writing the code, this shouldn't be much of a problem.
What about include all possible fields in the filter but restricting their usage by field name param. Like this:
select * from offers where
('name'=:field and name = :value)
OR
('field2'=:field and field2 = :value)
OR
('field3'=:field and field3 = :value)
I don't know how You can implement it with spring (I mean use variable column names) but I can suggest to use the following principle.
Keep your query like template:
String sql = "select * from offers where ##field = :value";
And every time before execution replace ##value parameter with the column You want.
And then You are gone.

can hibernate hsql automatically map the query result to a class?

I wrote a hsql:
String queryString = "select t1.a, t1.b, t2.c from table1 t1, table2 t2 where t1.id = t2.id";
and then I have a class:
class test{
String a;
String b;
String c
....//other getter and setter
}
I tried:
List = getHibernateTemplate().find(queryString);
this doesn't work, when I use test object in jsp page, it will throw out exception.
I have to manually create a test object:
List<Object[]> list = getHibernateTemplate().find(queryString);
test.seta(list.get(0)[0]);
is it possible for hibernate to automatically map the class for me in hsql ?
If you have a mapping for both table1 and table2 (see Prashant question above) you can do something like:
String queryString = "select t1 from table1 t1
inner join t1.table2 t2";
After you run the query you should have a list of t1 objects.
for(Table1 t1:listOfTable1Objects) {
t1.getA(); //for example or whatever you want to do with your object.
}
The Problem is that you do not write a HQL query. You just write a normal SQL query. In HQL, because the hibernate make the mapping from table to class, you cannot make a projection. So, if you write something like
String query = "FROM Class1 WHERE ome_condition;
without the SELECT clause, the Hibernate will be able to convert the result in the proper object.
You can see more about this here: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/queryhql.html
If you dont have a mapping, you may create a auxiliary class for this. Say ResultClass. Then you add #NamedNativeQuery and #SqlResultSetMapping annotations to the class:
#NamedNativeQuery(name="queryHehehe", query="select t1.field1 f1, t2.field2 f2 from table1 t1, table2 t2", resultSetMapping="mappingHehehe")
#SqlResultSetMapping(name="mappingHehehe", entities={
#EntityResult(entityClass=my.clazz.AuxiliaryClass.class, fields = {
#FieldResult(name="id", column="f1"),
#FieldResult(name="other_property", column="f2")
}),
})
public class AuxiliaryClass {
public Long id;
public String other_property;
}
I have never used this, but can work. Good luck.
If you need a query to return values from multiple tables and create an object of an unmapped class, then you need to either do what you're doing here, or use a ResultTransformer.
In order to do this with HibernateTemplate, you'll need to change the way you use the template, possibly using execute(HibernateCallback action), as you'll need to convert the sql query to a Criteria as described in Hibernate Reference Native SQL Chapter.
If you do want to try this, you'll probably want to use an AliasToBeanResultTransformer or AliasToBeanConstructorResultTransformer rather than writing your own transformer.

Categories