d2jsp
Log InRegister
d2jsp Forums > Off-Topic > Computers & IT > Programming & Development > Java: Interacting With A Database
Prev123Next
Add Reply New Topic New Poll
Member
Posts: 11,881
Joined: Aug 17 2007
Gold: 0.00
Nov 10 2014 07:33pm
Quote (Minkomonster @ Nov 10 2014 08:34pm)
The interface serves as a contract between the application layer and the data access layer. By using this contract, the data access layer is bound to provide implementations for the members it has defined in the interface. This means the application layer can utilize this contract in place of an actual instance of the data access layer. And because of this, the data access layer is free to change its underlying implementation without directly affecting the application layer. The only time the application layer must make changes is if the contract (interface) changes.

This also allows for modularity. Now you can swap implementations of that interface in and out to provide new/different functionality. Above I mentioned CustomerDAOImpl would access a database using jdbc. Well, what if you didn't want to access the database. What if you were writing unit tests, and needed a mocked version of the database? Well how about this

Code
public MockCustomerDAO implements CustomerDAO
{
    private List<Customer> customers = new List<Customer> { new Customer("Minko"), new Customer("Midnight") };

      public List<Customer> getCustomers() { return customers; }

        private Customer get(String id)
        {
            for(Customer c : customers)
                  if(c.getId().equals(id)) return c;
            return null;             
        }
public Customer getCustomerById(String id)
        {
            return get(id);           
        }

        //this method makes no sense because I only have 1 property on the Customer class
public boolean updateCustomer(Customer c)
        {
            Customer cust = get(c.getId());
           
              if(cust == null) return false;
             
              //set all of cust properties
              cust.setId(c.getId());

              return true;
             
        }

public boolean insertCustomer(Customer c)
        {
            customers.add(c);
            return true;
        }

public boolean deleteCustomer(Customer c)
        {
            c = get(c.getId());
            return customers.remove(c);
        }
   
}


Now you have a mocked version of your database which operates off of a static list. You can now just inject this in place of your normal implementation for unit testing.


I think I understand...
Feels as though my lack of experience with OO is hindering my ability to grasp the benefits of the abstraction.

Put simply, I do not see things in terms of objects, and I'm not sure I'll be able to do until I start using them.

As I was going along here, I wasn't even going to implement a student class.

My main method was going to:
-Prompt the user for an ID
-Check the ID to see if the user actually exists (obviously would never fly for authentication in the real world)
-Ask the user for an action (View, Enroll, Delete)
--Use the DbInteraction class to run the appropriate SQL.

This post was edited by MidnightRider on Nov 10 2014 07:34pm
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Nov 10 2014 07:53pm
Your solution would work. And would be easy to implement in the way you described. However, you would be tying the application layer directly to the datasource. And not just any datasource, the specific implementation of the data source. This is what your professor was talking about when he was recommending (or requiring?) the separation of layers.

By abstracting the implementations away, and exposing an interface instead, the application doesn't care what the datasource is. It just knows about the methods available on the interface (contract) given to it. And that it can use this interface to interact with a datasource. Right now you are using JDBC to connect to a database. What happens if next time your professor wants you to modify your application to utilize an XML data store instead? Well, you would have to rewrite the application layer, because you coupled it to a specific implementation of a datasource. If instead you exposed an interface, then all you would have to do is implement that interface on a new class that utilized an XML data store, and inject it and be done.

The choice is yours. Like I said, your attack plan will solve your assignment. But it could be better. And based on your original post, it seems like your professor wants you to steer away from that approach (it is what any newbie programmer would do). I am here if you are interested in exploring other options. I am gonna go take a nap though cause I am fucking drained. But if you are still working on this when I come back, just reply with your questions. If not me then someone else will pop in.

By the way, I am not a Java programmer. I know what JDBC is, but I have nevevr used it. So if you have specific questions, you may want to direct them to careblanche. Otherwise, I will only be able to give you high level explanations of it.
Member
Posts: 32,925
Joined: Jul 23 2006
Gold: 3,804.50
Nov 10 2014 10:01pm
You have three general options for connection management.

1) create connection/statement at the beginning of a transaction, perform transaction, then close all resources (result set, statement, connection) after the transaction. This means doing it in every single method of your DAO.
2) use a library/framework to handle connection pooling
3) manually reuse connection/statement in your code without closing them

i wouldn't worry about pooling for your purposes, and manually reusing them leads to bad practices and potential problems, especially for a beginner. so i recommend you take option 1.

Notice your code below? you're reusing the connection and you're not closing your result set, statement, or connection.
Code
public boolean validID() throws SQLException
{
this.setQuery("Select s.ID FROM STUDENT s WHERE s.ID = ?");
this.pStmt = conn.prepareStatement(this.query);
this.pStmt.setInt(1, this.studentID);

ResultSet rs = this.pStmt.executeQuery();
return rs.isBeforeFirst();
}


Example of what to change it to:
Code
public boolean isValidId(long id) throws SQLException{
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;

try{
con = getConnection(); // write this method which has the connection string, username, password inside
ps = con.prepareStatement("Select 1 FROM STUDENT WHERE ID=?");
ps.setLong(1, id);
rs = ps.executeQuery();
boolean isValid = rs.isBeforeFirst();

log.debug("isValidId(" + id + ")=" + isValid);
return isValid;

} catch (SQLException e){
throw e;
} catch (Exception e){
log.error(e);
} finally{
close(rs, ps, conn);
}
}


A couple of points to notice.
1) the connection/statement/resultset variables are declared OUTSIDE of the try/catch. this is done so the variables are in scope for the finally
2) always close the result set, statement, and connection when you no longer need it
3) always close them inside a finally block. if an error occurs when the connection is open but before you close it manually inside of the try, then the cleanup might not execute.
4) use PreparedStatement instead of Statement. i see you already used it, which is good.
5) always include some kind of logging. it saves you from debugging your stuff.

close is a shortcut. it's really annoying that closing a ResultSet, Statement, and Connection can all throw exceptions. here's the code for it:
Code
public static void close(ResultSet rs, Statement s, Connection con) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
log.error(e);
}
}
if (s != null) {
try {
s.close();
} catch (SQLException e) {
log.error(e);
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
log.error(e);
}
}
}


this method returned a simple boolean. if you needed to return more data (eg: name, id, class, etc etc), then you assign data from the result set into a Data Transfer Object, eg: Student. then return the Student or List of Student objects as needed.

i am not a fan of using instance variables for things like this.query and this.studentId. the query should only exist inside the method it's used, and studentId should be a parameter IMO. each method should be useable on its own without side effects or dependencies. the main reason it's not static is because, as Minko pointed out, it's best to put them behind an interface + factory. (static methods don't get along with interfaces)


i personally use long instead of int because i've been screwed by it in the past. but for your assignment, int should be fine. i use log4j for my logging, but you can wrap System.out.println if you wish.

ex:
Code
public static void log(String message){
if (true) // alternatively, check for some flag/config/etc
{
System.out.println(message);
// alternatively write to a file
}
}

then put your log statements everywhere you want. when you're ready to turn it in, you dont want the logging turned on, so just change true to false or whatever you want. if you directly put System.out.println(..) everywhere, you have to manually remove every single one of them when you're done.

This post was edited by carteblanche on Nov 10 2014 10:19pm
Member
Posts: 11,881
Joined: Aug 17 2007
Gold: 0.00
Nov 10 2014 11:15pm
Thanks for all of the help thus far.
I really appreciate it. <3

So, if I'm understanding this correctly:

For a robust application designed to interface with a data source , I need the following:
1. A class for each entity in my database - no SQL included.
1. A DAO for each entity in my database, which is actually an interface
2. For each distinct data source and for each DAO, a concrete class that actually uses JDBC (or similar query vehicle).


@carteblanche

Dumb question: From where are the methods getting the connection?
Having a hard time visualizing.

Potentially less dumb question: Can I use try with resources to avoid the finally clause?



Member
Posts: 32,925
Joined: Jul 23 2006
Gold: 3,804.50
Nov 10 2014 11:42pm
Quote (MidnightRider @ Nov 11 2014 12:15am)
1.  A class for each entity in my database - no SQL included.
1.  A DAO for each entity in my database, which is actually an interface
2.  For each distinct data source and for each DAO, a concrete class that actually uses JDBC (or similar query vehicle).

the concrete class is just an implementation of the DAO.
DAO doesn't have to be 1:1 with your entities.

Quote
Dumb question:  From where are the methods getting the connection? 
Having a hard time visualizing.


i just mocked it as getConnection() with a comment if you noticed:
Code
con = getConnection(); // write this method which has the connection string, username, password inside


here's one implementation:
Code
private Connection getConnection() throws SQLException{
Class.forName("com.mysql.jdbc.Driver"); // only needs to be done once, but it doesnt hurt anything.
return DriverManager.getConnection("jdbc:mysql://hostname/ databaseName", "username", "password");
}


Quote
Potentially less dumb question:  Can I use try with resources to avoid the finally clause?


You can. it's similar to C#'s "using" clause. i personally do not use it for two reasons:
1) habit. this is new in java 7, and i learned java 1.4. so i've been using the classical try/catch for years before try-with-resources was introduced. we don't even use java 7 at my work; all the app servers still use either java 1.4 for legacy apps or java 6
2) readability. iirc you can only put stuff in the try(...) block if it implements the AutoCloseable interface. that means you can't stick log statements inside there, nor PreparedStatement.setXXX methods. so you might need multiple try(...) blocks. i personally want to avoid nesting try blocks inside other try blocks. i prefer to refactor them into other methods. especially when you gotta keep indenting it on a small monitor.

you're more than welcome to use it if it tickles your fancy.

This post was edited by carteblanche on Nov 10 2014 11:48pm
Member
Posts: 32,925
Joined: Jul 23 2006
Gold: 3,804.50
Nov 11 2014 12:38am
Quote (MidnightRider @ Nov 10 2014 07:07pm)
And... I'm already confused    :unsure: 

On a very high level, I understand the value of interfaces, but looking at what you have, I'm already lost as to how I want to incorporate something like this in my code.


Quote
Feels as though my lack of experience with OO is hindering my ability to grasp the benefits of the abstraction.


jdbc is actually a great example of the power of interfaces. if you noticed, Connection, PreparedStatement, and ResultSet are all interfaces. you can pretty much plug-and-play any DBMS and keep the code essentially the same. the implementation is handled by the jdbc driver. that's where the Class.forName(...) comes in.
Member
Posts: 11,881
Joined: Aug 17 2007
Gold: 0.00
Nov 11 2014 04:26pm
If people aren't sick of these questions :D

How should I go about letting users add classes to their schedules?

Should I use SQL to check if the class exists, find its capacity, compare the current enrollment to the capacity, etc...

Or does one just let the database encounter an error and deal with it from there?
Member
Posts: 1,995
Joined: Jun 28 2006
Gold: 7.41
Nov 11 2014 04:28pm
Quote (MidnightRider @ Nov 11 2014 05:26pm)
If people aren't sick of these questions  :D

How should I go about letting users add classes to their schedules?

Should I use SQL to check if the class exists, find its capacity, compare the current enrollment to the capacity, etc...

Or does one just let the database encounter an error and deal with it from there?


Your first approach is the better choice.

Member
Posts: 11,881
Joined: Aug 17 2007
Gold: 0.00
Dec 3 2014 12:08am
I'm back ^_^


How does one usually handle null results after querying a database?

Unrealistic design here, but for the sake of leaning, students access my database by entering an ID (no password, no validation, etc.).
If my StudentDAO retrieves a null value after searching for a student based on the provided ID (i.e. the result set has no entries), any subsequent operations I attempt on the student object are going to fail.

Should I do something with the null value in the controller, or let it return the null value and deal with it elsewhere?


Code
public static void main(String[] args) {

Scanner in = new Scanner(System.in);
String userType = getUserType(in);
if(userType.equals("S"))
{
int studentID = getAccessID(in);
StudentController studentManager = new StudentController();
Student student = studentManager.findByID(studentID);
System.out.println(student.getlName());

}


Code
public class StudentController {

private StudentDAO studentDAO = new StudentDAO();

public Student findByID(int ID)
{
Student student = null;
try
{
HiberUtil.beginTransaction();
student = studentDAO.findByID(Student.class, ID);
HiberUtil.commitTransaction();

}
catch(HibernateException e)
{
System.out.println("Error");
System.out.println(e.getMessage());
}

if (student == null)
{
]What does one usually do!!?
}

return student;
}


This post was edited by MidnightRider on Dec 3 2014 12:09am
Member
Posts: 32,925
Joined: Jul 23 2006
Gold: 3,804.50
Dec 3 2014 12:22am
your business layer will throw an exception, and your presentation layer will catch it and tell the user that the id is invalid and needs to be entered again.
Go Back To Programming & Development Topic List
Prev123Next
Add Reply New Topic New Poll