I'm doing my own DAO classes using plain JDBC as an exercice.
Now, does something like an SQL LIKE fit into the DAO pattern ?
What I'd like to do is support a search feature which matches a field against a string. Is there another approach that is more suitable to OOP ?
A function with a good comment can fit your need :)
class MyDAO {
/* Search user which name contains searchName */
public List<User> searchUserByName(String searchName) {
String sql = "select * from USERS where USERNAME like '%" + searchName + "%'";
List<User> users = new ArrayList<User>();
// Execute sql,
// open ResultSet,
// iterate it and for every ResultRow's row convert to object User
// Add converted object User to users
// Free resource
return users;
}
}
This is just an idea, you need transaction management, connection supplier and so on
Related
I've a database with many thousands of tables that have been (and continue to be) created with a naming strategy - one table per calendar day:
data_2010_01_01
data_2010_01_02
...
data_2020_01_01
All tables contain sensor data from the same system in the same shape. So a single entity (lets call it SensorRecord) will absolutely map to all tables.
I'd imagined something like this would work:
#Query(nativeQuery = true, value = "SELECT * FROM \"?1\"")
Collection<SensorRecord> findSensorDataForDate(String tableName);
But it does not, and reading around the topic seems to suggest I am on the wrong path. Most posts on dynamic naming seem to state explicitly that you need one entity per table, but generating thousands of duplicate entities also seems wrong.
How can I use JPA (JPQL?) to work with this data where the table name follows a naming convention and can be changed as part of the query?
Parameters are only allowed in the where clause.
You can create custom repository method returns collection of SensorRecord dto. No need to map so many entities. You should get List<Object []> as query result and manually create dto objects.
#Autowired
EntityManager entityManager;
public List<SensorRecord> findSensorDataForDate(LocalDate date) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy_MM_dd");
String tableName = "data_" + date.format(formatter);
Query query = entityManager.createNativeQuery(
"select t.first_column, t.second_column from " + tableName + " t");
List<Object[]> queryResults = query.getResultList();
List<SensorRecord> sensorRecords = new ArrayList<>();
for (Object[] row : queryResults) {
SensorRecord record = new SensorRecord();
record.setFirstParameter((Integer) row[0]);
record.setSecondParameter((String) row[1]);
sensorRecords.add(record);
}
return sensorRecords;
}
Could it be just syntax error?
This has worked for me:
#Query(value = "select * from job where job.locked = 1 and job.user = ?1", nativeQuery = true)
public List<JobDAO> getJobsForUser(#Param("user") String user);
I have a web application that I am trying to "break".There's a login page that requires username and password input. Let's say I have a table Auser that stores username's info in MySQL.
When I hit Login after keying the credentials,it executes this line of code:
String sql = "select object(o) from Auser as o where ausername='" + username + "'";
Now, I know not using preparedStatement makes SQL query vulnerable to SQL injection and I want to perform such a stunt. I created a dummy table called test for the purpose of able to drop this table via the injection command.
I tried various ways like in my username input(root is the username):
root` DROP TABLE test;
And it didn't work. Is there a way to make my injection successful?
Update:
Just extra info, my username column is VARCHAR(255) and my method for getting the username is below:
public Auser get(String username, boolean moreInfo) {
try {
Auser u = null;
String sql = "select object(o) from Auser as o where ausername='" + username + "'";
List resList = em.createQuery(sql).getResultList();
if (resList == null) { // null check for sql query / library error
msg = CoreUtil.wrapMsg(CoreUtil.FUNC_ERROR,
this.getClass().getName(), "get[" + username + "]", "query error AUSER.");
} else if (resList.isEmpty()) {
msg = "User " + username + " not found.";
} else {
u = (Auser) resList.get(0);
}
return u;
} catch (Exception e) {
msg = CoreUtil.wrapMsg(CoreUtil.FUNC_ERROR,
this.getClass().getName(), "get[" + username + "]", e.getMessage());
return null;
}
}
Seems every solution, I tried keeps throwing IllegalArgumetnException and the table still remains.I just want to exploit the vulnerabilities of my program,it can be any kind of injection whether dropping a table, returning all users info,etc.
The EntityManager has some (very) basic protection built in that won't run more than one command in the same SQL statement.
This will protect you from Robert'); DROP TABLE Students; --, but it won't protect from attackers trying to expand/alter the one query that's being run.
For example, in your code an attacker could get the details of another user by entering the username ' OR 1 = 1 --; This would make the SQL string being executed
select object(o) from Auser as o where ausername='' OR 1 = 1 --'
which will select every user in the table (note that the -- at the end of the input will comment out everything after the injected code), and your method will return the first user in the result list This will potentially give the attacker details about another user that they should not have access to. If the first account is an administrator account then they may also have access they should not have.
An attacker can also learn the structure of the table this way - they can try strings like ' and IS_ADMIN = IS_ADMIN --, or ' OR ID = 0 --. If they try enough of these (and attacks like this can be easily automated) they will find valid column names when the query doesn't throw an error. They can potentially then make a more targeted injection attack to gain access to an admin account.
They might also learn things from the error message returned from a failed attempt, such as the DB platform, which can make attacks easier.
String sql = "select object(o) from Auser as o where ausername='" + username + "'";
If you want to delete the test table
username = "x'; DROP TABLE test AND '1'='1"
If you want to see all fields of all ausers entries
username = "x' OR '1'='1"
when i run my query in database visualizer its working perfectly, but i think there are some issues in syntax when i convert it in my DAO class method.
I want to get whole data against the name provided
In Visualizer:
SELECT first_name,last_name,nic,phone,email FROM x_hr_user where (first_name = 'Irum');
Now in Dao
public List<XHrUser> findXHrUserByNameInTable()
{
String name ="Irum";
Query query = em.createQuery("SELECT xHrNewUserObj.firstName,xHrNewUserObj.lastName, xHrNewUserObj.nic, xHrNewUserObj.phone, xHrNewUserObj.emil FROM XHrUser xHrNewUserObj where (xHrNewUserObj.firstName) = (name)");
List<XHrUser> list = query.getResultList();
return list;
}
Instead of showing single row, it displays whole data Table
Thank you
Your current query is not valid JPQL. It appears that you intended to insert the raw name string into your query, which could be done via a native query, but certainly is not desirable. Instead, use a named parameter in your JPQL query and then bind name to it.
String name = "Irum";
Query query = em.createQuery("SELECT x FROM XHrUser WHERE x.firstName = :name")
.setParameter("name", name);
List<XhrUser> list = query.getResultList();
You have to write query as below. where : is used for variable
Query query = em.createQuery("SELECT xHrNewUserObj.firstName,xHrNewUserObj.lastName, xHrNewUserObj.nic, xHrNewUserObj.phone, xHrNewUserObj.emil FROM XHrUser xHrNewUserObj where (xHrNewUserObj.firstName) = :name");
I am trying to write this query in order to authenticate the username and password of my API but I am getting error column not found. Both username and passwords are strings and I am using MYSQL database. I think there is a error with quotations as username and password are strings. How can I rectify the below code or is there a better way to write the same.
P.S - I am using spring MVC and this is my first project.
#Override
public TypeInfo getRole(final TypeInfo typeinfo) {
String sql =
"select Role from registartion where UserName=\"" + typeinfo.getUserName() + "and Password=\"" + typeinfo.getPassword() + "/"";
return jdbcTemplate.query(sql, new ResultSetExtractor<TypeInfo>() {
#Override
public TypeInfo extractData(ResultSet rs)
throws SQLException, DataAccessException {
if (rs.next()) {
System.out.println("VALID USER");
TypeInfo typeinfo1 = new TypeInfo();
typeinfo1.setUserName(typeinfo.getUserName());
typeinfo1.setPassword(typeinfo.getPassword());
typeinfo1.setRole(rs.getString("Role"));
return typeinfo1;
}
System.out.println("Not A valid user");
return null;
}
});
}
I am getting a error that "select Role from registartion where UserName=******" column name ******* not found.
That's not the way you should write your query.
JdbcTemplate uses an Object[] array as parameters, to avoid SQL injection.
code it somewhere in the lines of this:
String user = "yourUser";
String password = "yourPassword";
final String sql = "SELECT * from FOO where username = ? and password = ?";
Object[] sqlParameters = new Object[]{user, password};
List<YourEntityClass> list = getJdbcTemplate.query(sql, new BeanPropertyRowMapper<Your Entity Class>(YourEntityClass.class), sqlParameters);
BeanPropertyRowMapper actually maps the values for you. just make sure your entity class has the same property names as the ones on your database
more info here:
jdbcTemplate examples
The proper solution would be to use a PreparedStatement, in order to avoid having to mess with quoting and enhance security.
If you really must construct the statement by string concatination, you should note that string literals in SQL are denoted by single quotes ('), not double quotes ("):
String sql =
"select Role from registartion where UserName='" + typeinfo.getUserName() + "' and Password='" + typeinfo.getPassword() + '";
Try this.
String sql = "select Role from registartion where UserName='" + typeinfo.getUserName() + "' and Password='" + typeinfo.getPassword() + "'";
Change the double quotes (including the escape characters) to single quotes.
Close the single quote enclosing the user name (typeinfo.getUserName()). You need to keep a space between the closing single quote and the subsequent string.
If it still does not work then check the table names and column names. Maybe it is 'registration' and not 'registartion'? Or may be it is 'user_name' and not 'username'?
Tips for beginners: Copy paste the sql string into any database browser, replace the variables with actual values and execute. Check for any errors. It is easier to fix errors this way.
And lastly, use parameterized sql queries to avoid sql injection. In my opinion parameterized queries reduces syntax errors too.
You have to use single qoutes around the column values:
"select Role from registartion where UserName='" + typeinfo.getUserName() + "' and Password='" + typeinfo.getPassword() + "'";
You should better use PreparedStatement. It is easier to read and safer (prevents sql injection).
I am using
List<USERS> user =
getHibernateTemplate().find("select uid, username,email from USERS");
to get three columns values from the users TABLE. But I can access no individual column value using the "user" object because the "user" type is an object type and I can't cast it to the USERS.
Is there any ways to use the "user" object and access individual columns value?
Why are you just querying selected columns - just get the whole row(s). Let me know if that helps.
If you are fetching only few columns, the hibernate template will return a list of object arrays.
Your example should look like this,
List<Object[]> userDetails =
getHibernateTemplate().find("select uid, username,email from USERS");
And you should know the first element is a integer and second, third are string and do cast on your own. This is very error prone ofcourse.
Thanks Nilesh and Sean for your suggestions. I always deal with the objects instead of individual columns. But this specific app works with other tables from another app which is not written in Java (That is why I am using USERS table not "User", because it is already created by another app) and is not using hibernate. I created a USERS class that implements UserDetails and has much less columns than the original app USERS table. When I get the whole object I get a formatting error that is why I tried using selected columns instead of the object.Anyhow I wrote this code and was able to get the individual columns:
List user=
getHibernateTemplate().find("select uid, username,email from USERS where uid<>0 AND obj_type=1");
List<USERS> l = new ArrayList<USERS>();
for (int i = 0; i < user.size(); i++) {
USERS du = new USERS();
Object[] obj = (Object[]) user.get(i);
Integer uid = (Integer) obj[0];
du.setUid(uid);
String username = (String) obj[1];
du.setUsername(username);
String email = (String) obj[2];
du.setEmail(email);
l.add(du);
}
My last question: isn't it more expensive to get the whole columns(the object) than getting the individuals ones?
Keep in mind it that...
getHibernateTemplate.find() method returns List of based on passed object.
Then after this you have to take List of Users then you have to separate all resulting object and after specified a object you can access attribute of it.
Its very easy..
If you have any query then tell me
I will try my best.
#Override
public Object findByNo(Object id) throws Exception {
List list = getHibernateTemplate().find(
"select book from Book book " +
"where book.id=?",id);
if (list.size() != 0) {
return list.get(0);
} else {
return new Book();
}
}
I'm guessing your db table is called USERS and the entity class is called User. If that is the case, then you should do something like this:
List<User> users = getHibernateTemplate().find("from User");
for(User user: users){
// you probably don't need the id, so I'll skip that
String email = user.getEmail();
String userName = user.getUserName();
// now do something with username and email address
}
If you use an ORM framework, deal with objects, not with Database Columns!
And about naming:
Java naming conventions suggest that a class name is in TitleCase (or more precisely UpperCamelCase), not UPPERCASE. Also, a class that represents a single user should be called User, not Users.
you can try something like this:
for (TemplateAttributes templateAttributes1 : templateAttributes) {
templateAttributes1.setTemplates(templates);
templateAttributes1.setCreateDate(new Date());
templateAttributes1.setCreateUser(ApplicationConstants.userName);
templateAttributes1.setLastModified(new Timestamp(new Date().getTime()));
templateAttributesNew.add(templateAttributes1);
}
templates.setTemplateAttributes(templateAttributesNew);
templates.setUpdateDate(new Date());
templates.setUpdateUser(ApplicationConstants.userName);
templates.setLastModified(new Timestamp(new Date().getTime()));
getHibernateTemplate().bulkUpdate("delete from TemplateAttributes where templateAttributePK.templates.id="+templates.getId());
getHibernateTemplate().update(templates);
Try this
List<USERS> user =(List<USERS>)(List<?>)
getHibernateTemplate().find("select uid, username,email from USERS");