Returning Null vs Exception vs Contract - java

What would be considered an acceptable way of dealing with returning a record from a DB with the following 3 potential outcomes:
Db connection works, finds a user and returns a populated user object
Db connection works, doesn't find a user, returns a new user object
Db connection/query fails...
I'm for the most part aiming for design by contract:
class Scratch {
public User getUser(int id) {
try {
// Prepare SQL Query
PreparedStatement s = this.connection.prepareStatement(
"select * from get_user(?)"
);
// Provide SQL Parameters
s.setInt(1, id);
// Run our SQL
ResultSet rs = s.executeQuery();
rs.next();
// Extract data into Entity
User user = User.createFromDatabase(rs);
rs.close();
return user;
} catch(Exception e) {
e.printStackTrace();
}
return new User();
}
}
In the situation that the DB connection or query fails it's a little less obvious what I should do, I have a few options:
Return a new user object, because our method has agreed to return a user
Pro: This sticks to designing by contracts
Con: This makes it look like the user doesn't exist.
Return null since it didn't actually get a user.
Pro: This is totally clear that a user was not found
Con: Requires a null check
Throw an exception further up the chain.
Pro: Makes it explicitly clear the operation wasn't achieved
Con: Does not attempt to rectify issues where they happen
I'm leaning towards handling Exceptions as it avoids the billion-dollar mistake, and also maintains design by contract in normal situations.
However I'd like to know if there are any obvious pitfalls or if this is a familiar situation with a well established pattern to resolve it.

I would throw an exception and let the user know that the connection failed. Returning NULL is something I would never do because you won't know what the problem was.
I don't know why you would return "New user object" if you don't have connection to the database. You won't be able to save the user.
My choice will be to Throw an exception

Use Optional (please). This make explicit that the user could not be found in the database and avoid NullPointerException's.
Optional(User) if the user is found in the database
Empty if user is not found
If there is an Error you have two options:
Notify the client so it can get feedback and react to this failure. In this case throw an exception and handle it in the proper layer. This should be the default behaviour for most of the applications.
Hide it to the client, this is less common but some times the client doesn't care or you don't want to publish your failures. In that case just return an Empty.

Related

How do you overwrite a OneToMany mapping with JPA?

I have a One to many mapping in JPA as follows:
In my blockchain class I have the #OneToMany annotation over ArrayList (which is the "chain" property) for which I have a Block class.
I have a method for replacing the chain of a blockchain instance with another when a new chain is broadcast or new block is broadcast on the wire (e.g. pubsub, triggred by async listener). The method replaces the chain if it is found to be valid and of sufficient length.
The chain is reflected in the database as a join table between blockchain and block. When a new chain comes in, it will be mostly identical. In other words there will be at least 1 collision with primary key, if only its the genesis block. More likely all but one or a few blocks at the tip will collide, so I want the ability to handle this without incident. Ideally JPA would have figured out how to do it without me with the following code, but that's not the case.
#Override
public boolean replaceChain(String name, ArrayList<Block> new_chain) throws NoSuchAlgorithmException, ChainTooShortException, GenesisBlockInvalidException, BlocksInChainInvalidException {
this.connect();
em.getTransaction().begin();
Query query = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain = (Blockchain) query.getSingleResult();
blockchain.replace_chain(new_chain);
em.getTransaction().commit();
this.disconnect();
return true;
}
From there I tried many permutations and tricks I could think of. I tried manually deleting each block that is a duplicate from the block entity but then it had a problem with the join table and stack overflow said apparently JPA is not set up to manage that manually. It's not the OOP way. I'm fine with that, but then my question is what is the OOP way. Just to overwrite a one to many relationship. The new incoming OneToMany should overwrite everything, displace everything else and that's it, but it tries to duplicate. I read other SO posts but I either didn't understand them well enough or they didn't see to help.
I'm running this through a DAO service, wired up to work through a pubnub listener callback. I have two servers and in fact codebases running- this Main "node on the network" that is dealing with the database (port 8080) and an "in memory" one on 9080 that starts with only the genesis block and if it gets a 200 GET request to 8080 will clone that and replace that chain. Replace chain works- just not to write to database. I call the second node on the network the PEER instance. It has the ability to mine blocks and when it does, it broadcasts to pubsub which triggers the main node. That's my setup. Everything seems to be working beautifully except the JPA part of it. I'm using Eclipselink and Tomcat.
From my understanding, when you start a transaction with entitymanager, it basically watches what you do and takes notes and records the results and does its magic, kind of like a scribe or observer. You set it up to watch, and then you do your business and then you tell it to commit, it still has limits and constraints to deal with or exceptions will be thrown but that's my understanding and that's the route I initially went.
Here is my error log for just this code above, not the trying to manually delete the blocks of a given chain. I could do that but I couldn't get to the join table and I know that's not the ideal way
Error Code: 1062
Call: INSERT INTO block (TIMESTAMP, DATA, DIFFICULTY, HASH, LASTHASH, NONCE) VALUES (?, ?, ?, ?, ?, ?)
bind => [1617166967254, [B#5ebbe21e, 9, 002057d17e0de9c5f97f6a0f3e7534c0599036ae307ece2ee3f645025c153f80, 007e833b320c58bcf29096e22ced52a5c90c915e23830eeae0a7093290af4080, 246]
Query: InsertObjectQuery(privblock.gerald.ryan.entity.Block#d6f817c0)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
at privblock.gerald.ryan.dao.BlockchainDao.replaceChain(BlockchainDao.java:97)
at privblock.gerald.ryan.service.BlockchainService.replaceChainService(BlockchainService.java:38)
at pubsub.PubNubSubCallback.message(PubNubSubCallback.java:132)
at com.pubnub.api.managers.ListenerManager.announce(ListenerManager.java:61)
at com.pubnub.api.workers.SubscribeMessageWorker.processIncomingPayload(SubscribeMessageWorker.java:228)
at com.pubnub.api.workers.SubscribeMessageWorker.takeMessage(SubscribeMessageWorker.java:83)
at com.pubnub.api.workers.SubscribeMessageWorker.run(SubscribeMessageWorker.java:74)
at java.base/java.lang.Thread.run(Thread.java:832)
Any help or insight is appreciated!
I figured it out. It seems to work with one extra annotation property orphanRemoval=true as below
#OneToMany(targetEntity = Block.class, cascade = CascadeType.PERSIST, orphanRemoval=true)
#JoinTable(name = "BlocksByChain")
List<Block> chain; // The chain itself
I knew it had to be simple and some feature that already existed. It was just not the framework default
EDIT: Not quite. Not perfectly. I still have to have code that flushes it out inside the DAO, and that is clunky and not optimal. Also I get a console output about a deadlock or something. I didn't notice that before as my application works as expected, but I know there has to be a better way.
This code also has to exist for it to work:
#Override
public boolean replaceChain(String name, ArrayList<Block> new_chain) throws NoSuchAlgorithmException,
ChainTooShortException, GenesisBlockInvalidException, BlocksInChainInvalidException {
this.connect();
em.getTransaction().begin();
Query query = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain = (Blockchain) query.getSingleResult();
// blockchain.replace_chain(new_chain);
// em.merge(blockchain);
System.out.println("GOING TO REPLACE CHAIN AS SERVICE");
// THIS LONG BLOCK IS BECAUSE I COULDN'T FIND A MORE NATURAL WAY. I KEEP GETTING
// ERRORS.
// I JUST WANT TO OVERWRITE THE CHAIN OR DO A SMART MERGE
// INSTEAD IT TRIES TO APPEND. I HAVE TO WRITE AN EMPTY SET TO DB AND COMMIT IT
// AND THEN REPOPULATE IT. ALTERNATELY I COULD MAYBE DO A NATIVE QUERY AND
// TRUNCATE
// REGARDLESS IT DOESN'T SEEM TO SMARTLY MERGE THE TWO CHAINS
// -- IT SHOULD BE EASY WHEN THE NEW CHAIN IS AN EXTENSION, VS A FORK
// -- HANDLING THE "FORK" POTENTIAL OF BLOCKCHAIN ADDS TO THE COMPLEXITY IN
// WHICH CASE EASIEST TO TRUNCATE AND START FRESH
// Try Flush
if (blockchain.willReplace(new_chain)) {
blockchain.setChain(null);
em.getTransaction().commit();
em.getTransaction().begin();
// em.flush();
Query query2 = em.createQuery("select b from Blockchain b where b.instance_name = :name");
query.setParameter("name", name);
Blockchain blockchain2 = (Blockchain) query.getSingleResult();
blockchain2.setChain(new_chain);
em.getTransaction().commit();
this.disconnect();
return true;
}
em.getTransaction().commit();
this.disconnect();
return true;
}

rs.next() returns false in java

the code is login there are entries in my database my also query is working fine but whenever i put any data valid or invalid it just shows email or pass incorrect my rs.next() just returns false. I also verified jdbc connection it works fine. Used mySql databse.
package bean;
import java.sql.*;
public class LoginDao {
public static boolean validate(LoginBean bean){
boolean status=false;
try{
Connection con=ConnectionProvider.getCon();
PreparedStatement ps=con.prepareStatement("select * from user432 where email=? and pass=?");
ps.setString(1,bean.getEmail());
ps.setString(2, bean.getPass());
ResultSet rs=ps.executeQuery();
status=rs.next();
}catch(Exception e){}
return status;
}
}
What it looks like to me is that all this method is meant to do is validate the actual existence of a particular User which contains the supplied E-Mail address and Password. Fair enough but you claim that you always get returned false from the method. The obvious reason for that would be the fact that your query found no results otherwise rs.next() would be true.
Well, it could be the extra curly bracket at the end of your method but let's just assume you just goofed there when copy/pasting the code into your post.
It could however be that bean.getEmail() and bean.getPass() are not supplying what you think it is, I don't know since we can't see the bean class. Maybe what you should try is bean.getEmail().toString() and bean.getPass().toString().
If your code is producing an actual Exception then you're hiding it by maintaining a empty catch block. There's a real good chance that an exception of some sort is thrown and therefore providing a false result from your method but you'll never know what it is since you have nothing in place to view it. You should really have the exception as a SQLException rather than just Exception but hey, that's not the end of the world, and within the catch block you should at least put something, maybe: System.out.println("What the....sumthin wrong with ma code!!!"); or even better yet: e.printStackTrace();.
Curious...is the Database Table name really user432? Or is that a User Name?

Proper way to insert record with unique attribute

I am using spring, hibernate and postgreSQL.
Let's say I have a table looking like this:
CREATE TABLE test
(
id integer NOT NULL
name character(10)
CONSTRAINT test_unique UNIQUE (id)
)
So always when I am inserting record the attribute id should be unique
I would like to know what is better way to insert new record (in my spring java app):
1) Check if record with given id exists and if it doesn't insert record, something like this:
if(testDao.find(id) == null) {
Test test = new Test(Integer id, String name);
testeDao.create(test);
}
2) Call straight create method and wait if it will throw DataAccessException...
Test test = new Test(Integer id, String name);
try{
testeDao.create(test);
}
catch(DataAccessException e){
System.out.println("Error inserting record");
}
I consider the 1st way appropriate but it means more processing for DB. What is your opinion?
Thank you in advance for any advice.
Option (2) is subject to a race condition, where a concurrent session could create the record between checking for it and inserting it. This window is longer than you might expect because the record might be already inserted by another transaction, but not yet committed.
Option (1) is better, but will result in a lot of noise in the PostgreSQL error logs.
The best way is to use PostgreSQL 9.5's INSERT ... ON CONFLICT ... support to do a reliable, race-condition-free insert-if-not-exists operation.
On older versions you can use a loop in plpgsql.
Both those options require use of native queries, of course.
Depends on the source of your ID. If you generate it yourself you can assert uniqueness and rely on catching an exception, e.g. http://docs.oracle.com/javase/1.5.0/docs/api/java/util/UUID.html
Another way would be to let Postgres generate the ID using the SERIAL data type
http://www.postgresql.org/docs/8.1/interactive/datatype.html#DATATYPE-SERIAL
If you have to take over from an untrusted source, do the prior check.

Java and MySQL query

I have problem with java and MySQL. My code:
Connection connection;
// ...
for (String query : updateAndInsertQuery) {
Statement stm = connection.createStatement();
stm.execute(query);
stm.close();
}
Statement stm2 = connection.createStatement();
System.out.println("Before query");
System.out.flush();
ResultSet Result = stm2.executeQuery(selectQuery);
System.out.println("After query");
System.out.flush();
int vfrom, vto;
while (Result.next()) {
// ...
}
When I run program i see in MySQL queries and run
show processlist;
selectQuery is visible on list wth status Sending data or Writing to net. On console print: Before query. Next
show processlist;
returns empty list, but application don't print After query. Do you have similar problem?
-- edit
I resolve my problem.
I think:
wen MySQL returns data and query isn't visible on processlist in MySQL
I should immediately get on console message: After query
but console was empty, java process works (cpu usage was 90-100%) so I think it was my mistake, but after 1h application throws Exception
Increase memory limit resolve my problem.
So I have next question why application throw exception after hour? Garbage collection try dealocate unised objects?
executing queries manually usually leads into many different problems - all of which are platform-specific and DB-specific. I think your best answer will be : "switch to ORM".
This framework has proven to be exceptionally good, wrappig all your SQL-data into Entities and transactions (if required) will resolve most of your problems at the same time - you will only need to annotate your entities and relationships correctly. Database-queries can be executed via JPA-"criteria"s which are platform-independent AND allow you to avoid a lot of problems as well as making your code READABLE.
Tutorial : http://www.vogella.com/tutorials/JavaPersistenceAPI/article.html
SO-question : https://stackoverflow.com/questions/743065/what-is-the-fastest-way-to-learn-jpa
With JPA, you wont need to care about statements or queries anymore (well, at least most of the time) and your mentioned problem will disappear - PLUS : it only takes 30-60min to implement.
Additional tip : use Maven & Eclipselink (JPA2 implementation) - thats a very powerful, portable combination

hibernate fails to retrieve objects from DB

I'm writing a project for college and I've encountered some strange phenomena.
The program supposed to serve a restaurant so it has a server side that manages all the needs of the different front ends. the different front ends are "dinner terminal", "kitchen terminal", "waiter terminal" and an "admin terminal".
When I add an object to the DB I see it in the DB and the kitchen terminal receives it and I see that the object it gets is the right one.
public void addSessionOrder(String id, SessionOrder sessionOrder)
{
Session context = clientSessions.get(id);
context.beginTransaction();
context.save(sessionOrder);
context.getTransaction()
.commit();
}
notice that each terminal (connection) has it's own session in hibernate.
however, once I try to update the status of a sessionorder i get this exception
java.lang.NullPointerException
at database.DatabaseContext.updateSessionOrderStatus(DatabaseContext.java:170)
at protocol.handlers.UpdateSessionOrderStatusHandler.handle(UpdateSessionOrderStatusHandler.java:35)
at server.ResturantServer.handleClientMessage(ResturantServer.java:126)
at server.ConnectionManager.handleClientMessage(ConnectionManager.java:86)
at server.SockJSSocketHandler$3.handle(SockJSSocketHandler.java:55)
this is the method:
public void updateSessionOrderStatus(String id, SessionOrder order, OrderStatus newStatus)
{
Session context = clientSessions.get(id);
context.beginTransaction();
SessionOrder ord = (SessionOrder)context.get(SessionOrder.class, order.getOrderId());
ord.setStatus(newStatus);
context.update(ord);
context.getTransaction()
.commit();
}
The line that throws the exception is "ord.setStatus(newStatus);"
after debugging this is the info I have:
fields id and sessionOrder contain legit data and are initiated as needed.
sessionOrder.getOrderId() returns the needed ID for a corresponding object in the DB (it exists)
the query on the DB return null to ord.
Another thing I've noticed, if I turn the server off (kill hibernate) and restart everything, the whole things works fine. So I believe it has something to do with the fact that some session X inserted the object to the DB, and some other session Y tries to retrieve it afterwards.
the sessions are different and originate from different connections, and the specific order exists in the DB so I see no reason for it not to return the value.
I think it has something to do with caching of the model, however I'm quite a noob in hibernate so I can't pin point the problem.
sessionOrder.getOrderId() returns the needed ID for a corresponding object in the DB (it exists) the query on the DB return null to ord.
The ord object is null so it throws a NPE. I guess that hibernate cannot find the order with the given ID.
Add some logging here and you'll see what causes you trouble

Categories

Resources