How to identify non thread-safe code in a multi-threaded environment? - java

I have designed and implemented a simple webstore based on traditional MVC Model 1 architecture using pure JSP and JavaBeans (Yes, I still use that legacy technology in my pet projects ;)).
I am using DAO design pattern to implement my persistence layer for a webstore. But I am not sure if I have implemented the classes correctly in my DAO layer. I am specifically concerned about the QueryExecutor.java and DataPopulator.java classes (mentioned below). All the methods in both these classes are defined as static which makes me think if this is the correct approach in multithreaded environment. Hence, I have following questions regarding the static methods.
Will there be synchronization issues when multiple users are trying to do a checkout with different products? If answer to the above question is yes, then how can I actually reproduce this synchronization issue?
Are there any testing/tracing tools available which will actually show that a specific piece of code will/might create synchronization issues in a multithreaded environment? Can I see that a User1 was trying to access Product-101 but was displayed Product-202 because of non thread-safe code?
Assuming there are synchronization issues; Should these methods be made non-static and classes instantitable so that we can create an instance using new operator OR Should a synchronized block be placed around the non thread-safe code?
Please guide.
MasterDao.java
public interface MasterDao {
Product getProduct(int productId) throws SQLException;
}
BaseDao.java
public abstract class BaseDao {
protected DataSource dataSource;
public BaseDao(DataSource dataSource) {
this.dataSource = dataSource;
}
}
MasterDaoImpl.java
public class MasterDaoImpl extends BaseDao implements MasterDao {
private static final Logger LOG = Logger.getLogger(MasterDaoImpl.class);
public MasterDaoImpl(DataSource dataSource) {
super(dataSource);
}
#Override
public Product getProduct(int productId) throws SQLException {
Product product = null;
String sql = "select * from products where product_id= " + productId;
//STATIC METHOD CALL HERE, COULD THIS POSE A SYNCHRONIZATION ISSUE ??????
List<Product> products = QueryExecutor.executeProductsQuery(dataSource.getConnection(), sql);
if (!GenericUtils.isListEmpty(products)) {
product = products.get(0);
}
return product;
}
}
QueryExecutor.java
public final class QueryExecutor {
private static final Logger LOG = Logger.getLogger(QueryExecutor.class);
//SO CANNOT NEW AN INSTANCE
private QueryExecutor() {
}
static List<Product> executeProductsQuery(Connection cn, String sql) {
Statement stmt = null;
ResultSet rs = null;
List<Product> al = new ArrayList<>();
LOG.debug(sql);
try {
stmt = cn.createStatement();
rs = stmt.executeQuery(sql);
while (rs != null && rs.next()) {
//STATIC METHOD CALL HERE, COULD THIS POSE A SYNCHRONIZATION ISSUE ???????
Product p = DataPopulator.populateProduct(rs);
al.add(p);
}
LOG.debug("al.size() = " + al.size());
return al;
} catch (Exception ex) {
LOG.error("Exception while executing products query....", ex);
return null;
} finally {
try {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (cn != null) {
cn.close();
}
} catch (Exception ex) {
LOG.error("Exception while closing DB resources rs, stmt or cn.......", ex);
}
}
}
}
DataPopulator.java
public class DataPopulator {
private static final Logger LOG = Logger.getLogger(DataPopulator.class);
//SO CANNOT NEW AN INSTANCE
private DataPopulator() {
}
//STATIC METHOD DEFINED HERE, COULD THIS POSE A SYNCHRONIZATION ISSUE FOR THE CALLING METHODS ???????
public static Product populateProduct(ResultSet rs) throws SQLException {
String productId = GenericUtils.nullToEmptyString(rs.getString("PRODUCT_ID"));
String name = GenericUtils.nullToEmptyString(rs.getString("NAME"));
String image = GenericUtils.nullToEmptyString(rs.getString("IMAGE"));
String listPrice = GenericUtils.nullToEmptyString(rs.getString("LIST_PRICE"));
Product product = new Product(new Integer(productId), name, image, new BigDecimal(listPrice));
LOG.debug("product = " + product);
return product;
}
}

Your code is thread-safe.
The reason, and the key to thread-safety, is your (static) methods do not maintain state. ie your methods only use local variables (not fields).
It doesn't matter if the methods are static or not.

Assuming there are synchronization issues; Should these methods be made non-static and classes instantitable so that we can create an instance using new operator
This won't help. Multiple threads can do as they please with a single object just as they can with a static method, and you will run into synchronization issues.
OR Should a synchronized block be placed around the non thread-safe code?
Yes this is the safe way. Any code inside of a synchronized block is guaranteed to have at most one thread in it for any given time.
Looking through your code, I don't see many data structures that could possibly be shared amongst threads. Assuming you had something like
public final class QueryExecutor {
int numQueries = 0;
public void doQuery() {
numQueries++;
}
}
Then you run into trouble because 4 threads could have executed doQuery at the same moment, and so you have 4 threads modifying the value of numQueries - a big problem.
However with your code, the only shared class fields is the logging class, which will have it's own thread-safe synchronization built in - therefore the code you have provided looks good.

There is no state within your code (no mutable member variables or fields, for example), so Java synchronisation is irrelevant.
Also as far as I can tell there are no database creates, updates, or deletes, so there's no issue there either.
There's some questionable practice, for sure (e.g. the non-management of the database Connection object, the wide scope of some variables, not to mention the statics), but nothing wrong as such.
As for how you would test, or determine thread-safety, you could do worse than operate your site manually using two different browsers side-by-side. Or create a shell script that performs automated HTTP requests using curl. Or create a WebDriver test that runs multiple sessions across a variety of real browsers and checks that the expected products are visible under all scenarios...

Related

Java - Asynchronous multi-database with connection poll

I am developing a program that, based on a configuration file, allows different types of databases (e.g., YAML, MySQL, SQLite, and others to be added in the future) to be used to store data.
Currently it is all running on the main thread but I would like to start delegating to secondary threads so as not to block the execution of the program.
For supported databases that use a connection, I use HikariCP so that the process is not slowed down too much by opening a new connection every time.
The main problem is the multitude of available databases. For example, for some databases it might be sufficient to store the query string in a queue and have an executor check it every X seconds; if it is not empty it executes all the queries. For others, however, it is not, because perhaps they require other operations (e.g., YAML files that use a key-value system with a map).
What I can't do is something "universal", that doesn't give problems with the order of queries (cannot just create a Thread and execute it, because then one fetch thread might execute before another insertion thread and the data might not be up to date) and that can return data on completion (in the case of get functions).
I currently have an abstract Database class that contains all the get() and set(...) methods for the various data to be stored. Some methods need to be executed synchronously (must be blocking) while others can and should be executed asynchronously.
Example:
public abstract class Database {
public abstract boolean hasPlayedBefore(#Nonnull final UUID uuid);
}
public final class YAMLDatabase extends Database {
#Override
public boolean hasPlayedBefore(#Nonnull final UUID uuid) { return getFile(uuid).exists(); }
}
public final class MySQLDatabase extends Database {
#Override
public boolean hasPlayedBefore(#Nonnull final UUID uuid) {
try (
final Connection conn = getConnection(); // Get a connection from the poll
final PreparedStatement statement = conn.prepareStatement("SELECT * FROM " + TABLE_NAME + " WHERE UUID= '" + uuid + "';");
final ResultSet result = statement.executeQuery()
) {
return result.isBeforeFirst();
} catch (final SQLException e) {
// Notifies the error
Util.sendMessage("Database error: " + e.getMessage() + ".");
writeLog(e, uuid, "attempt to check whether the user is new or has played before");
}
return true;
}
}
// Simple example class that uses the database
public final class Usage {
private final Database db;
public Usage(#Nonnull final Database db) { this.db = db; }
public User getUser(#Nonnull final UUID uuid) {
if(db.hasPlayedBefore(uuid))
return db.getUser(uuid); // Sync query
else {
// Set default starting balance
final User user = new User(uuid, startingBalance);
db.setBalance(uuid, startingBalance); // Example of sync query that I would like to be async
return user;
}
}
}
Any advice? I am already somewhat familiar with Future, CompletableFuture and Callback.

Java: Use reflection or store instance objects in list?

how are you? I'm trying to do some dynamic method calls to get sql strings on various objects in Java (Android), but i'm stuck with some questions about performance and stability.
Context Example: Repository class onCreate method get all entity objects (tables) and call a method (getCreateTable for example) to get a sql string to execute.
Sure i can explicit call class by class caling each method, but i have other calls like "dropTables", "truncateTables" and etc, and i do not want to be repeating the same structure all the time.
public void CreateTables() {
execute(Entity1.getCreateTable());
execute(Entity2.getCreateTable());
execute(Entity3.getCreateTable());
[..]
execute(Entity50.getCreateTable());
}
public void DropTables() {
execute(Entity1.getDropTable());
execute(Entity2.getDropTable());
execute(Entity3.getDropTable());
[..]
execute(Entity50.getDropTable());
}
Until now i know i can do that in 3 diferent ways.
1) Using reflection (currently in use): Basicaly, i store all the objects class in a list, and then use the reflection to call the desired static method.But i know that reflection not always should be the first choice.
private final List<Class> entityList = new ArrayList<Class>() {
{
add(Entity1.class);
add(Entity2.class);
add(Entity3.class);
}
};
public void createTables() {
/* get all query strings */
List<String> queryList = getQueryList("createTable");
try {
for (String query : queryList) {
execute(query);
}
} catch (SQLException e) {
[...]
}
}
private List<String> getQueryList(String methodName) {
List<String> queryList = new ArrayList<>();
for (Class<?> objectClass : entityList) {
try {
Method[] ms = objectClass.getMethods();
for (Method me : ms) {
if (me.getName().equals(methodName)) {
String query = (String) me.invoke(null);
if (query != null && query.length() > 0) {
queryList.add((String) me.invoke(null));
}
break;
}
}
} catch (Exception e) {
[...]
}
}
return queryList;
}
2) Storing object instance in list: I can have a list with the objects instanced and then cast then into abstract parent class (or interface) and call the methods to get the sql string. In this case, i don't know if is a good practice to keep an list of instanced objects in memory, maybe this could be worst than use reflection depending on list size.
private final List<BaseEntity> entityList = new ArrayList<BaseEntity>() {
{
add(new Entity1(context));
add(new Entity2(context));
add(new Entity3(context));
}
};
public void createTables() {
for (BaseEntity entity : entityList) {
try {
execute(entity.getCreateTable());
} catch (Exception e) {
[...]
}
}
}
3) Storing all the strings into JSON object: I don't tested that one yet, but i'm sure with should work. I can call an "init" method to iterate over all objects and create that JSON object/array with all the sql strings (drop, create, truncate and etc).
I really appreciate if you share with me what you think about these approaches (pros and cons) or another better solution.
As pointed out in the comments, it was a clarly a bad design (it's an old project that i'm refactoring). So i decided to get away from reflection and spend some time to redesign the code itself.
I created an base super class to handle all similiar methods and let the entities/models implement only the required individual rules, so DB access is stored in only one class as a Singleton. It's far better to use interface polymorphism.
In this way, the db class handle the dinamic SQL generation to avoid repeating the same code everywhere and re-use/recycle the list of instances to improve performance.
Obs. 1: Reflection throw down performance and usually let debbuging harder. Sure it can save some time as it is fast to implement, but will disable most of the IDE features, which makes it worthless in most cases.
Obs. 2: Keeping a list of DB instances active should never be done either. It's never a good idea to have many instances access database simultaneously, it can cause DB to lock and reproduce unexpectad issues.
Obs. 3: That JSON thing... forget about it. I'm sorry to suggest something so ugly.

getSessionAsSigner is null while using frostillic.us controller classes

I will start by saying that:
I'm using ODA (godmode,khan,marcel).
I'm the only code signer.
sessionAsSigner is working the first time I load an XPage that calls it.
sessionAsSigner becomes null after I reload a page (cmd + R) but not when I'm subsequently referencing it in any action during in the context of the viewScope lifetime.
I'm implementing #Gidgerby 's concept of controller classes
I would also add that sessionAsSigner works consistently if I just prepare a simple XPage that does the following:
<p>
<xp:text value="session: #{session.effectiveUserName}" />
</p>
<p>
<xp:text value="sessionAsSigner: #{sessionAsSigner.effectiveUserName}" />
</p>
Now, I am not using SSJS. I'm JSF/EL oriented as much as I can, according to my current knowledge. So, the way I access Domino back-end is unconventional for a Domino XPages programmer.
Where I can't get getSessionAsSigner to work consistently is when I try to do the above mentioned thing...
Here is my XPage controller (backing bean):
public class TestPageController extends StandardXPageController {
private static final long serialVersionUID = 1L;
private AnswerDTO answer;
public TestPageController() {
loadQuotation();
}
private void loadQuotation() {
AnswerDAO answerDAO = Factory.createAnswerDAO();
try {
answer = answerDAO.read("doc_id");
} catch (Exception e) {
e.printStackTrace();
}
}
public AnswerDTO getAnswer() {
return answer;
}
}
AnswerDTO is a POJO. I'm using the DAO/DTO design pattern.
The AnswerDAO implementation - with simplified code (wrap method is just a mere mapping of fields) - as following:
public class AnswerDAODominoImpl implements AnswerDAO {
private transient Session s;
private transient Database db;
private Session getSession() {
if (s == null) {
s = XSPUtil.getCurrentSessionAsSigner();
}
return s;
}
private Database getDatabase() throws NotesException {
if (db == null) {
db = getSession().getDatabase("server_name", "server_path");
}
return db;
}
public AnswerDTO read(String id) throws Exception {
Database db = getDatabase();
return wrap(db.getDocumentByID(id));
}
}
This is the ViewHandler class:
public class ViewHandler extends ViewHandlerExImpl {
public ViewHandler(final javax.faces.application.ViewHandler delegate) {
super(delegate);
}
#SuppressWarnings("unchecked")
public UIViewRoot createView(final FacesContext context, final String pageName) {
try {
// pageName is the XPage name prefixing the slash (e.g. "/home")
String pageClassName = pageName.substring(1);
Class<? extends XPageController> controllerClass = null;
try {
controllerClass = (Class<? extends XPageController>) context.getContextClassLoader().loadClass(
"com.sea.xsp.controller." + StringUtil.getProperCaseString(pageClassName) + "PageController");
} catch (ClassNotFoundException cnfe) {
controllerClass = StandardXPageController.class;
}
XPageController controller = controllerClass.newInstance();
Map<String, Object> requestScope = (Map<String, Object>) context.getApplication().getVariableResolver().resolveVariable(context, "requestScope");
requestScope.put("controller", controller);
UIViewRootEx root = (UIViewRootEx) super.createView(context, pageName);
root.getViewMap().put("controller", controller);
requestScope.remove("controller");
// MethodBinding beforePageLoad = context.getApplication().createMethodBinding("#{controller.beforePageLoad}", new Class[] { PhaseEvent.class });
// root.setBeforePageLoad(beforePageLoad);
return root;
} catch (Exception e) {
e.printStackTrace();
}
return super.createView(context, pageName);
}
}
Basically, what the viewhandler does is to check the existence of a java class which prefix is the XPage name itself.
eg. test.xsp = something.something.package.TestPageController
This approach allows me to forget about declaring specific XPage related classes as generic managed beans in the faces-config.xml
All the XPages will get an easy handle to their corresponding backing bean that will always be named #{controller}
Now, having that being said if I simply write the following in an XPage, everything will work the first time, but not a second time (getSession() is OK, getSessionAsSigner is null), never ever again. I need to push a new change to the database (design update after any change to the java code and xsp.application.forcefullrefresh=true) and it will work again, but, again, just the first time the page is loaded.
<p>
<xp:text value="answer doc id: #{controller.answer.id}" />
</p>
Ideas?
This is possibly due to a bug we discovered a bit ago with the XPages runtime, somehow related to ClassLoader#loadClass. It turns out that using that as you do (and as I used to) can cause sessionAsSigner to stop working after the first page load. The fix is to switch to Class.forName(String, true, ClassLoader): https://github.com/jesse-gallagher/XPages-Scaffolding/commit/d65320fd6d98ff2fbaa814a95eb38ce7bad5a81d
What happens if you run through in Java debugger? Is it going into XSPUtil.getSessionAsSigner() the second time round?
I tend to use ExtLibUtil.resolveVariable() to get a handle on sessionAsSigner if godmode is enabled. Alternatively, there's org.openntf.domino.utils.Factory.getSessionAsSigner(). Do those give a different result?
(In RC3 that is still be available but is marked deprecated in favour of Factory.getSession(SessionType.SIGNER) because we're supporting implementations outside XPages, so there are more session types involved.)

How to make thread safe singleton class which can accepts parameter?

I am trying to make a class as ThreadSafe Singleton but somehow I am not able to understand how to make ThreadSafe Singleton class which can accepts parameter.
Below is the class which I am using from this github link which I am using currently to make a connection to Zookeeper -
public class LeaderLatchExample {
private CuratorFramework client;
private String latchPath;
private String id;
private LeaderLatch leaderLatch;
public LeaderLatchExample(String connString, String latchPath, String id) {
client = CuratorFrameworkFactory.newClient(connString, new ExponentialBackoffRetry(1000, Integer.MAX_VALUE));
this.id = id;
this.latchPath = latchPath;
}
public void start() throws Exception {
client.start();
client.getZookeeperClient().blockUntilConnectedOrTimedOut();
leaderLatch = new LeaderLatch(client, latchPath, id);
leaderLatch.start();
}
public boolean isLeader() {
return leaderLatch.hasLeadership();
}
public Participant currentLeader() throws Exception {
return leaderLatch.getLeader();
}
public void close() throws IOException {
leaderLatch.close();
client.close();
}
public CuratorFramework getClient() {
return client;
}
public String getLatchPath() {
return latchPath;
}
public String getId() {
return id;
}
public LeaderLatch getLeaderLatch() {
return leaderLatch;
}
}
And this is the way I am calling the above class -
public static void main(String[] args) throws Exception {
String latchPath = "/latch";
String connStr = "10.12.136.235:2181";
LeaderLatchExample node1 = new LeaderLatchExample(connStr, latchPath, "node-1"); // this I will be doing only one time at just the initialization time
node1.start();
System.out.println("now node-1 think the leader is " + node1.currentLeader());
}
Now what I need is if I am calling these two below methods from any class in my program, I should be able to get an instance of it. So I am thinking to make above class as a Thread Safe Singleton so that I can access these two methods across all my java program.
isLeader()
getClient()
How do I make above class as ThreadSafe singleton and then make use of isLeader() and getClient() across all my classes to see who is the leader and get the client instance..
I need to do this only at the initialization time and once it is done, I should be able to use isLeader() and getClient() across all my classes.. Is this possible to do?
// this line I will be doing only one time at just the initialization time
LeaderLatchExample node1 = new LeaderLatchExample(connStr, latchPath, "node-1");
node1.start();
This is more of Java question not Zookeeper stuff..
A singleton which requires a parameter is a bit of a contradiction in terms. After all, you'd need to supply the parameter value on every call, and then consider what would happen if the value was different to an earlier one.
I would encourage you to avoid using the singleton pattern at all here. Instead, make your class a perfectly normal one - but use dependency injection to provide a reference to a single configured instance to all your classes that need it.
That way:
The singleton nature isn't enforced, it's just a natural part of you only needing one reference. If later on you needed two references (e.g. for different Zookeeper instances for some reason) you can just configure the dependency injection differently
The lack of global state generally makes things much easier to test. One test might use one configuration; another test might use a different one. No singleton, no problem. Just pass the relevant reference into the constructor of the class under test.

Criticise/Recommendations for my code [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
Before i go any further it would be nice to know if there is any major design flaws in my program so far. Is there anything worth changing before i continue?
Model
package model;
import java.sql.*;
import java.util.*;
public class MovieDatabase {
#SuppressWarnings({ "rawtypes", "unchecked" })
public List queryMovies() throws SQLException {
Connection connection = null;
java.sql.Statement statement = null;
ResultSet rs = null;
List results = new ArrayList();
try {
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
statement = connection.createStatement();
String query = "SELECT * FROM movie";
rs = statement.executeQuery(query);
while(rs.next()) {
MovieBean bean = new MovieBean();
bean.setMovieId(rs.getInt(1));
bean.setTitle(rs.getString(2));
bean.setYear(rs.getInt(3));
bean.setRating(rs.getInt(4));
results.add(bean);
}
} catch(SQLException e) {
}
return results;
}
}
Servlet
public class Service extends HttpServlet {
#SuppressWarnings("rawtypes")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Movies!");
MovieDatabase movies = new MovieDatabase();
try {
List results = movies.queryMovies();
Iterator it = results.iterator();
while(it.hasNext()) {
MovieBean movie = new MovieBean();
movie = (MovieBean)it.next();
out.println(movie.getYear());
}
}
catch(SQLException e) {
}
}
}
Bean
package model;
#SuppressWarnings("serial")
public class MovieBean implements java.io.Serializable {
protected int movieid;
protected int rating;
protected int year;
protected String title;
public MovieBean() {
}
public void setMovieId(int movieidVal) {
movieid = movieidVal;
}
public void setRating(int ratingVal) {
rating = ratingVal;
}
public void setYear(int yearVal) {
year = yearVal;
}
public void setTitle(String titleVal) {
title = titleVal;
}
public int getMovieId() {
return movieid;
}
public int getRating() {
return rating;
}
public int getYear() {
return year;
}
public String getTitle() {
return title;
}
}
Here are a couple of suggestions:
Your MovieDatabase has the Connection creation embedded inside it. You don't use a Connection pool that way.
You embed the connection parameters (e.g., driver class, URL, etc.) inside your code. Best to externalize them.
You don't clean up any JDBC resources. This is guaranteed to bring you grief.
You have empty catch blocks. This is a heinous error. Log the stack trace. You'll have no way of knowing if anything is wrong as coded.
MovieBean? Names matter - make it Movie.
Your default constructor does nothing at all, and it's the only constructor you provide. Your String reference to title will be null. I think you should have one constructor that initializes all the fields properly.
Your Service should not extend Servlet. I think you should have a POJO interface and an implementation that has nothing to do with HTTP. You can't use this service (or test it) without the web.
Another empty catch block - you're asking for trouble. When will you learn to print the stack trace?
I wouldn't have a MovieDatabase; I'd go with a MovieDao interface that had CRUD operations, like this:
package persistence;
public interface MovieDao
{
List<Movie> find();
Movie find(int id);
List<Movie> find(String title);
void save(Movie movie);
void update(Movie movie);
void delete(Movie movie);
}
Much of the following is style, not necessarily the 'right' way, and certainly not the only way.
I'd move the database connection
to a try block in the servlet's #doGet. I'd pass the
connection to
MovieDatabase#queryMovies. The
reason is, what happens if in that same request you need
to do another query using another
class? Your connection is in
MovieDatabase and another class
would have no access to it. If you had a situation where both classes could update the database, you'd be unable to roll back the entire transaction. Not good.
I'd add a commit statement at the end of the 'success' path in #doGet
I'd add after try block containing the database connection an exception block, wherein I'd issue a rollback. So if there's an exception, a rollback would be performed every time.
I'd close the database connection in
#doGet's finally block. This is most important. edit - see the pseudocode below for an example
If you don't move the connection into the servlet, then straight
away you should close that
connection in #queryMovies' finally
clause.
If this were a larger project, I'd use Hibernate and its tools to
generate DAOs and models. Hibernate
would generate for you a class and
method that would return a
collection of MovieBeans to you.
You wouldn't have to do anything but
invoke it. Auto-generated database
access code is good.
I'd add a JSP and put the collection you're building into the request. Then your jsp could iterate over the collection and format it as appropriate. This moves the presentation of the information out of the servlet, which is a coordinator of action, not a formatter of data in the MVC model.
If you implemented the above suggestions, it would probably drop your number of lines of code by 50% or more. Learning Hibernate can be a headache, so it wouldn't necessarily be easier or faster the first time. The reason it reduces the lines of code (while doing pretty much the same work) is that generated code is pretty much right and coders don't have to worry about it.
I use the following pattern in my servlets all the time. This is pseudocode, not real java.
Connection conn = null;
try {
conn.getConnection(...);
// your implementation here
conn.commit();
} catch (Exception e) {
conn.rollback();
} finally {
conn.close();
}
The point is that the database connection can always be passed to workers, work is always committed unless something goes wrong. If something goes wrong, there's guaranteed to be a rollback. In either case, the database connection is closed when its all over.
It's pretty simple and straightforward, no big issues. The only thing I would point out is that you're doing a SELECT * then refer to the result set by column index. This is not a problem at this stage but if your schema changes (say, a field gets added in the middle) then your code will break. I would explicitly select the column names:
SELECT id, title, year, rating FROM movie
There are many things wrong (many have already pointed out most of them). Seems like the code is written in 90's. I strongly suggest you read about layered architecture, separation of concerns, MVC, DAO pattern. Then you will answer the question yourself and I will up vote your answer ;-).

Categories