One sql query to check for unique - java

I have a Spring Mvc application, in registration form i have an area for email and name
Before registering i check that all fields are unique in the DB with the following code in service layer (I'm using spring data)
#Override
public Optional<String> isUnuque(final Users user) {
if (this.repository.findByEmail(user.getEmail()) != null) {
return Optional.of("Your email is not unique");
} else if (this.repository.findByName(user.getName()) != null) {
return Optional.of("Your name is not unique");
}
return Optional.empty();
}
Then in controller i check if Optional is present. But with this code i make 2 requests to the DB, i can use native query like this to make only one request:
select * from users where name = 'dd' or email = 'ddd#mail.ru'
But how would i know which parameter is not unique name or email?
Thank in advance
BTW i solved this problem with the following code
#Override
public Optional<String> isUnuque(final Users user) {
final Users dbUser = this.repository.findByEmailOrName(user.getEmail(), user.getName());
if (dbUser != null) {
if (user.getEmail().equals(dbUser.getEmail())) {
return Optional.of("Not unique email");
} else {
return Optional.of("Not unique name");
}
}
return Optional.empty();
}
But if there are any sql query that can do exactly what i want without double checking in service layer please write it below

I'm not a java expert but as a database guy, I would say a better way would be to enforce the UNIQUE constraint on name and email in the table and handle the exception, which maintains the data integrity regardless of the application.
Another option is to get the count of emails and names with the given arguments.
SELECT count(CASE
WHEN name = :p_name
THEN 1
END) AS name_count
,count(CASE
WHEN email = :p_email
THEN 1
END) AS email_count
FROM users;
The COUNT function does not count nulls, so when condition in CASE is false( by default NULL), it adds 0 to the count. So, effectively the query would return name_count and email_count as 0 or 1 depending on the existing value, which you should be able to use in your code.

Union All can do this.
select 'name' from users where name = 'dd'
union all
select 'email' from users where name = 'ddd'#mail.ru

Related

Return null instead of org.hibernate.ObjectNotFoundException: Java

I have a table called ad_session which logs user sessions. I am using Java to get a list of all successful sessions from that table. I then loop through that list to get the user for each session (which is a foreign key to the ad_user table). I then get the client that belongs to that user, and I add the client to a list. However, one of the users no longer exists, so my code stops running and it gives throws the following exception:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [ADUser#76A5C22E6D2446A399AE9AD7C1DED0C7]
This is my original code:
List<Session> sessions = getAllSuccessfulSessionsInTable();
List<Client> clientsForThatDay = new ArrayList<>();
try {
for (Session session : sessions) {
//code fails when trying to get the non-existent user:
User user = session.getCreatedBy();
Client userClient = user.getClient();
clientsForThatDay.add(userClient);
}
} catch (Exception e) {
log.error("Error getting client from user: ", e);
}
I assumed that when getting a non-existent record, it would return null, so this is what I tried:
List<Session> sessions = getAllSuccessfulSessionsInTable();
List<Client> clientsForThatDay = new ArrayList<>();
//Create new user object to stand in place of the non-existent user
User deletedUser = new User();
deletedUser.setName("Deleted User");
//Create new client object to stand in place of the non-existent client
Client deletedUserClient = new Client();
deletedUserClient.setName("Unknown Client");
try {
for (Session session : sessions) {
//check is User is null, if it is, use the deletedUser object, otherwise, use the existing user
User user = session.getCreatedBy() == null ? deletedUser : session.getCreatedBy();
Client userClient = user.getName().equals("Deleted User") ? deletedUserClient : user.getClient();
clientsForThatDay.add(userClient);
}
} catch (Exception e) {
log.error("Error getting client from user: ", e);
}
However, it is not returning null, it's just throwing the exception and then stopping.
How can I get it to return null here so I can deal with the missing record without my code stopping?
Thanks in advance for any suggestions.
It seems that your database is missing a foreign key constraint.
This means that the table mapping User has a reference to a row in the table for Client that no longer exist.
This can only happen if a client has been deleted without updating the user table. The solution would be to add a foreign key constraint between the tables.
Keep in mind that if the data in your tables are not correct, when Hibernate loads the entity User, it will also believe there's a client. This means that User#getClient won't be null, and every place in the code where you have a check like user.getClient() == null is going to fail. A try-catch approach won't help you with this (unless you set the association to null in case of error, I guess).
The solutions I can think of:
Add the foreign key constraint (imho, the best solution)
Don't map the association, map client_id as an attribute and load the client using a second query or find (I would only do this if you cannot update the database)
class User {
#Column(name = "client_id")
Long clientId;
}
User user = ...
Client client = session.find(Client.class, user.getClientId());
You can load the client via session.find(Client.class, user.getClient().getId()) and set the association with the result:
User user = //...
Client client = session.find(Client.class, user.getClient().getId());
user.setClient(client);
Don't map the association at all in User, and run a native SQL query to load the client:
User user = ...
String sql = "select * from Client c join User u on c.id = u.client_id where u.id = :uid";
Client client = session.createNativeQuery(sql, Client.class)
.setParameter("uid", user.getId())
.getSingleResultOrNull();
You can pick what works best for you, but keep in mind that mapping an association without the foreign key constraint, will cause all sort of consistency issues.
I've decided to put option 3 only because, sometimes, people have some impossible situations at work, but I wouldn't recommend it.

java-How to perform SQL injection for testing purposes?

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"

Java EE, Integer to User?

I have recently asked quite similar question here, but answer does not solve my new problem.
I have two tables: User and Book, they are in ManyToOne relation. The Book table has attribute called user_id that connects both tables. Using Eclipse I generated entity classes, and "user_id" was created not as Integer like in database, but:
#JoinColumn(name="user_id")
private User user;
So now when i try to create new "Book" like this:
public String saveData() {
if(!validate()){
...
if (book != null) {
System.out.println(getUser());
setName(book.getName());
System.out.println("Post3");
setSurname(book.getSurname());
setAdress(book.getAdress());
setSize(book.getSize());
setContact(book.getContact());
setPrice(book.getPrice());
setOthers(book.getOthers());
setIsReady(book.getIsReady());
setRooms(book.getRooms());
setUser(book.getUser());
System.out.println(user);
}
private boolean validate() {
FacesContext ctx = FacesContext.getCurrentInstance();
boolean result = false;
System.out.println("validate");
if (ctx.getMessageList().isEmpty()) {
book.setName(name.trim());
book.setSurname(surname.trim());
book.setAdress(adress.trim());
book.setSize(size.trim());
book.setContact(contact.trim());
book.setPrice(price);
book.setOthers(others.trim());
book.setIsReady(isReady.trim());
book.setRooms(rooms);
book.setUser(user);
result = true;
}
return result;
}
...
bookDAO.create(book);
I'm getting
Column 'user_id' cannot be null
I am not sending 'user_id' in form but I have it stored in session BUT as integer.
So now when I am trying to force that int into the setUser I get an error that I can use only User objects there.
So my question is, are there any ways to convert Integer(which my id_user is) into the User?
You need to get User object from your database using the user_id in your session.
If you are using JPA entitymanager
User user = entityManager.find(User.class, user_id);
book.setUser(user);
You must select User by user_id from your database, or just
User user = new User();
user.setUserId(id_user);
book.setUser(user);

ActiveJDBC: save() generates insert only for auto-generated primary key?

I recently started using ActiveJDBC. I have the following table (postgresql)
CREATE TABLE users (
id uuid PRIMARY KEY,
email text UNIQUE NOT NULL,
password text,
<some more nullable columns>
created_at timestamp with time zone NOT NULL default (now() at time zone 'utc'),
updated_at timestamp with time zone
);
As you can see, the primary key is of type uuid and has no auto-generate value of any kind.
This is my User class which models the table :
public class User extends Model
{
public User() {
setId(UUID.randomUUID()); // sets value to primary key
}
...
}
This is my attempt to create a new row and insert it :
User u = new User();
System.out.println(u.saveIt());
actually, I expected the insert to fail, since I did not set any value for mandatory email column. However, simply got false as return value. when I turned on logging, I saw that the framework generated an update sql instead of insert:
[main] INFO org.javalite.activejdbc.DB - Query: "UPDATE users SET email = ?, ..., updated_at = ? WHERE id = ?", with parameters: <null>, <null>, <null>, <null>, <null>, <null>, <null>, <2016-01-07 17:30:46.025>, <0621fbdb-5b95-4ee7-a474-8ee9165e2982>, took: 1 milliseconds
so I looked at the save() method inside org.javalite.activejdbc.Model class and saw this piece of code:
if (getId() == null) {
result = insert();
} else {
result = update();
}
does this mean that id column has to be empty in order for an insert to be generated ? if this is true this is unacceptable, so I must be missing something.
#sharonbn, please, see this documentation page: http://javalite.io/surrogate_primary_keys.
ActiveJDBC depends on autogenerated IDs. If the ID == null, the frameworks assumes this is a new record, and generates INSERT statement. If it is non-null, it is assumed the record already exists, and generates UPDATE.
In your case, you will need to explicitly call user.insert() instead of user.saveIt(). This is an 'expert' mode in cases when developers want to be in control of ID management. Further, the method user.update() is private. So for you to insert a new record, you will be calling
user.insert();
and for updates:
user.save();
// or:
user.saveIt();
, depending on what you want.

Using Spring Hibernate getHibernateTemplate().find to to select columns

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");

Categories

Resources