I am working on a project that utilizes a database with several relations. We are working with tables such as:
Customers(id, f_name, l_name)
CreditCards(cardNum, expDate, securityCode)
BillingInfo(id, f_name, l_name, address, city, zip)
etc...
Using Java Database Connectivity, my question is, is there a common way one performs queries on these relations?
Even before getting to the "guts" of this project, I envision being able to call some method void insert(int id, String f_name, String l_name), a member method of a hypothetical CustomerQueryHandler class. This class would of course be accompanied by other methods such as a delete and update method.
I figured I'd start with an abstract class called QueryHandler:
import java.sql.*;
public abstract class QueryHandler {
protected String jdbcUrl = null;
protected String userid = null;
protected String password = null;
protected Connection conn;
QueryHandler(String url_in, String id_in, String pass_in) {
this.jdbcUrl = url_in;
this.userid = id_in;
this.password = pass_in;
try {
this.ds = new OracleDataSource();
} catch (SQLException e) {
e.printStackTrace();
}
ds.setURL(this.jdbcUrl);
try {
conn = ds.getConnection(userid, password);
} catch (SQLException e) {
e.printStackTrace();
}
}
public abstract void insert(/*not sure of signature*/);
public abstract void update(/*not sure of signature*/);
public abstract void delete(/*not sure of signature*/);
/* OTHER METHODS BELOW */
}
There would be a derived class for each table and each derived class would implement its own version of insert, update, and delete:
public class CustomersQueryHandler extends QueryHandler {
public CustomersQueryHandler(String url_in, String id_in, String pass_in) {
super(String url_in, String id_in, String pass_in);
}
...
This is actually the point where I became very confused because, does it make sense for this to be a superclass, even though each subclass will have its own parameter list for the aforementioned methods?
Related
I'm coming from PHP and moved to java. I'm asking myself (and you guys) if there is a way to implement someting like this:
I'm trying to implement a class/classes to create CRUD operations for many database entities. All entities inherit their functions (most of them from the parent)
I need to implement the tableName and idFieldName in the parent class DatabaseEntity to avoid compiler warnings.
It seems like java tries to use the parents properties (which are obviously null) because the function is implemented in the parent.
Is there a way to overcome this problem? Any feedback is greatly apreciated!
abstract class DatabaseEntity {
protected String tableName;
protected String idFieldName;
public DataRecord readFromDB(int recordID) throws SQLException {
...
String sqlStatement = String.format("SELECT * FROM %s WHERE %s = %s", this.tableName, this.idFieldName, recordID); // Exception shows this line
...
}
}
class DatabaseRecord extends DatabaseEntity {
protected String tableName = "DatabaseRecordTable";
protected String idFieldName = "ID";
public void getRecord() {
...
DataRecord record = this.readFromDB(1); // leads to java.lang.NullPointerException: null
...
}
}
Disclaimer: I'm new to github and I apreciate any feedback on improoving my posts :)
when you use the method readFromDBin which you refer to the tableNameand the idFieldName, these two fields remain nullas long as they are not initailized,
try to remove the fields from your abstract class and do something like this :
abstract class DatabaseEntity {
// protected String tableName;
// protected String idFieldName;
public abstract String getTableName();
public abstract String getIdFieldName() ;
public DataRecord readFromDB(int recordID) throws SQLException {
...
String sqlStatement = String.format("SELECT * FROM %s WHERE %s = %s",getTableName(), getIdFieldName(), recordID);
...
}
}
and the implementation would be like :
class DatabaseRecord extends DatabaseEntity {
protected String tableName = "DatabaseRecordTable";
protected String idFieldName = "ID";
public void getRecord() throws SQLException {
DataRecord record = this.readFromDB(1);
}
#Override
public String getTableName() {
return this.tableName;
}
#Override
public String getIdFieldName() {
return this.idFieldName ;
}
}
Im try to insert data into Database using ArrayList.there is a Erro msg.
That is my Custmer.class method. this is what i got from when i going to pass ArrayList into another class.
incompatible types: ArrayList<String> cannot be converted to ArrayList<Inquiries>
I want to know how to do this using correct Using OOP concept
public void passingMsg(ArrayList<Inquiries> arrlist){
try {
System.out.println("Method "+arrlist);
String sq = "INSERT INTO Inquiries (name,mail,tp,msg)VALUES(?,?,?)";
PreparedStatement pr = con.prepareStatement(sq);
for(int i=0;i<arrlist.size();i++){
pr.setString(1,arrlist.get(i).getName());
pr.setString(2,arrlist.get(i).getMail());
pr.setString(3,arrlist.get(i).getTp());
pr.setString(4,arrlist.get(i).getMsg());
}
pr.executeQuery();//executeBatch();
} catch (SQLException ex) {
}
}
and this is how i get values from user
String name = txtName.getText();
String mail = txtEmail.getText();
String tp = txtTp.getText();
String msg = txtMsg.getText();
ArrayList<String> arrInq = new ArrayList<String>();
arrInq.add(name);
arrInq.add(mail);
arrInq.add(tp);
arrInq.add(msg);
Custmer c =new Custmer();
if( c.passingMsg(arrInq)){
try {
JOptionPane.showMessageDialog(this, "Successs!!");
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Unsuccesss!!");
e.printStackTrace();
}
}
and this is my Inquiries.class :
public class Inquiries {
private String name;
private String mail;
private String tp;
private String msg;
public Inquiries(String name,String mail,String tp,String msg){
this.name = name;
this.mail = mail;
this.tp = tp;
this.msg = msg;
}
//
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getTp() {
return tp;
}
public void setTp(String tp) {
this.tp = tp;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
Can Some one please explain whats wrong with this. please ?
Reason For Error
This was simply telling you that your types were incompatible for the operation you were trying to perform. In your passingMsg() method, you have its header as: public void passingMsg(ArrayList<Inquiries> arrlist). However, inside your "how i get values from user" area, which I will now refer to as "2nd Snippet", you have your method call declared as: if( c.passingMsg(arrInq)). This means that you are implying that your parameter being passed, arrInq in this case, is of the type ArrayList<Inquiries>, but it's not. It's being initialized in your 2nd Snippet as: ArrayList<String> arrInq = new ArrayList<String>();
Simple Fix
I take no responsibility for this code; use at your own risk. To fix this, you would want to change that entire 2nd Snippet to something similar to the following:
String name = txtName.getText();
String mail = txtEmail.getText();
String tp = txtTp.getText();
String msg = txtMsg.getText();
ArrayList<Inquiries> arrInq = new ArrayList<Inquiries>();
arrInq.add(new Inquiries(name, mail, tp, msg));
Custmer c = new Custmer();
try {
c.passingMsg(arrInq);
JOptionPane.showMessageDialog(this, "Successs!!");
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "Unsuccesss!!");
e.printStackTrace();
}
You would also want to change the method header to either return a boolean, or fix it up a little bit to actually throw the exception. Such as:
public void passingMsg(ArrayList<Inquiries> arrlist) {
System.out.println("Method " + arrlist);
String sq = "INSERT INTO Inquiries(name,mail,tp,msg) VALUES(?,?,?)";
PreparedStatement pr = con.prepareStatement(sq);
for (Inquiries inquiries : arrlist) {
pr.setString(1, inquiries.getName());
pr.setString(2, inquiries.getMail());
pr.setString(3, inquiries.getTp());
pr.setString(4, inquiries.getMsg());
}
pr.executeQuery();//executeBatch();
}
Let's talk in O-O-P way.
Here Inquiries is your model, model is nothing but simple class that has instance members and public methods to get and set value of model's instance variable.
Generally we put all database related operations code in their respective models.
e.g. I have model "Model" which typically maps to database table say it as "TableModel" ,I would do something like this:
public class Model{
private int id;
private String attr;
//other properties of the model
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
//other getters and setters
//here we write methods to performs database operations
public void save(){
//use "this" to get properties of object
//logic to save to this object in database table TableModel as record
}
public void delete(int id){
//logic to delete this object i.e. from database table TableModel
}
public Model get(int id){
//retrieve record from table TableModel with this id
}
//other methods to get data from database.
}
Now question is how I can use this in some another class. Let's say I have list of Model objects and I wish to insert them in to database.I will do it something like this:
public class AnotherClass{
public void someMethod(){
//create list of models objects e.g. get them from user interface
ArrayList<Model> models=new ArrayList<>();
for(int i=0;i<3;i++){
Model model=new Model();
model.setId(i);
model.setAttr("attr"+i);
models.add(model);
}
SomeOtherClass obj=new SomeOtherClass();
obj.insert(models);
}
}
public class SomeOtherClass{
//other code above.....
//my method that inserts each Model object in database
//Note: this is sample method , you should do it in optimized way
// e.g. batch insert
public void insert(ArrayList<Model> models){
for(Model myModel:models){
myModel.save();
}
}
//other code below.....
}
You are using the wrong type parameter for the ArrayList. Instead of ArrayList<String> you need ArrayList<Inquiries>. To fix the problem, you should remove this code ...
ArrayList<String> arrInq = new ArrayList<String>();
arrInq.add(name);
arrInq.add(mail);
arrInq.add(tp);
arrInq.add(msg);
... and replace it with this code:
ArrayList<Inquiries> arrInq = new ArrayList<Inquiries>();
arrInq.add(new Inquiries(name, mail, tp, msg));
I was using multiple constructor from the same class, but a few days ago I got a 'rare' problem. The constructor initializes all the fields =(
I have Ejercicio.java class
public class Ejercicios {
int Id_Ejercicio;
String Descripcion;
String Finalidad;
String Duracion;
//Constructor
public Ejercicios() {
super();
}
//Constructor with 2 fields
public Ejercicios(int id_Ejercicio, String descripcion) {
super();
Id_Ejercicio = id_Ejercicio;
Descripcion = descripcion;
}
//Constructor with 4 fields
public Ejercicios(int id_Ejercicio, String descripcion, String finalidad, String duracion) {
super();
Id_Ejercicio = id_Ejercicio;
Descripcion = descripcion;
Finalidad = finalidad;
Duracion = duracion;
}
}
And the Lista_Ejercicios.java class
public class List_Ejercicios {
ArrayList<Ejercicios> lejer;
public List_Ejercicios() {
super();
}
}
And my principal class where I try to use these differents constructors
public Response Principal(SQLQuery){
List<Ejercicios> listaEJER = new ArrayList<Ejercicios>();
dbCon = new ConexionBD();
ResultSet rslt;
try {
conn = (Connection) ConexionBD.setDBConnection();
rslt = dbCon.getResultSet(SQLQuery, conn);
while(rslt.next()){
listaEJER.add(new Ejercicios( rslt.getInt(1),rslt.getString(2) ));
}
}catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//finally block code
return Response.ok(listaEJER.toArray()).build();
}
I was using 'Advanced Rest Client' for Chrome and I don't have an idea why I receive the 4 fields instead 2 like I especified in the constructor
...
{
descripcion: "Jump as high as you can"
id_Ejercicio: 1
finalidad: null
duracion: null
}
...
I have in trouble, these constructors work two weeks ago I don't have any clue why currently its running doesn't work.
They did not work two weeks ago. You've changed something. When you declare a field in your class, that field is always there. If you don't initialise it in your constructor, it will be auto-initialised. For classes, this default value is null, whereas for primitives it is 0, false etc. However, this behaves exactly the same as if you had initialised it to that value in the constructor - myEjercicios.Finalidad (use naming conventions please) will be null, as it is an auto-initialised object of type String.
As for fixing this issue, it shouldn't be hard to write some code to not print values that are null. If you want a different set of fields, however, you must declare two different classes (perhaps one extending the other).
I have found myself in the need to override a static method, simply because it makes most sense, but I also know this is not possible.
The superclass, Entity.java:
abstract public class Entity<T> {
public Entity() {
//set up database connection
}
abstract public static Map<Object, T> getAll();
abstract public void insert();
abstract public void update();
protected void getData(final String query) {
//get data via database
}
protected void executeQuery(final String query) {
//execute sql query on database
}
}
One of the many concrete implementations, Account.java:
public class Account extends Entity<Account> {
private final static String ALL_QUERY = "SELECT * FROM accounts";
private final static String INSERT_QUERY = "INSERT INTO accounts (username, password) VALUES(?, ?)";
private final static String UPDATE_QUERY = "UPDATE accounts SET password=? WHERE username=?";
private String username;
private String password;
public Account(final String username, final String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(final String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
#Override
public static Map<Object, Account> getAll() {
//return a map using the ALL_QUERY string, calls getData(string);
}
#Override
public void insert() {
//insert this using INSERT_QUERY, calls executeQuery(string);
}
#Override
public void update() {
//update this using UPDATE_QUERY, calls executeQuery(string);
}
}
I haven't been going in depth explaining the code, but any general feedback on it would also be appreciated, I hope the comments explain enough.
So basically I think we can all agree that using Account.getAll() makes more sense over new Account().getAll() (if I would introduce a dummy syntax for it).
However I do want to have it extend the Entity class, currently it is only for convienience, but later on I may have to use sets/lists/multisets of Entity and perform an update() action on all of them, for example if I would build some queue that performances all updates every minute.
So well, is there a way to construct getAll() correctly?
Regards.
You could have separate classes for operations on all elements:
abstract public class Collection<T extends Entity<T>> {
abstract public static List<T> getAll();
public void printAll() {
// Print all entries of List obtained from getAll()
}
}
Which you could use as:
public class Accounts extends Collection<Account> {
#Override
public List<Account> getAll() {
//return a list using the ALL_QUERY string, calls getData(string);
}
}
It doesn't seems to me that it is really "simply because it makes most sense".
Tying persistence at your entity is not a good idea. There are already lots of patterns that give an appropriate design on this problem.
For example, in Domain Driven Design, "Persistence Ignorance" is what people trying to achieve. Consider making a Repository for each of your entity:
interface Repository<T> {
List<T> findAll();
void insert(T);
void update(T);
}
so you can override it by whatever way you want:
interface UserRepository extends Repository<User> {
// some other methods which is meaningful for User
User findByLoginName(String loginName);
}
class UserRepositoryImpl implements UserRepository {
List<User> findAll() {
// call whatever query
}
void insert(T){...}
void update(T){...}
User findByLoginName(String loginName) {...}
}
With a proper design and a component to handle the retrieval/storage of entity, you can have a less-persistence-coupled entity, and with repository that can perform proper "overriding".
I have a HashMap that look something like this-
HashMap<String, TableConnectionInfo> tableList
which means it's value is a Class TableConnectionInfo which looks something like this-
public class TableConnectionInfo {
public String url;
public String user;
public String password;
public String driver;
public String suffix;
public String sql;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
}
So Suppose, If I have two values in the above HashMap. That means, I need to make two different connections to two different database. And suppose if that map has three values, then I need to make three different connections to three different database.
In the main thread, I am populating the above map by reading it from the properties file like this and after that this map won't get modified.
for (String arg : databaseNames) {
TableConnectionInfo ci = new TableConnectionInfo();
String url = prop.getProperty(arg + ".url");
String user = prop.getProperty(arg + ".user");
String password = prop.getProperty(arg + ".password");
String driver = prop.getProperty(arg + ".driver");
String suffix = prop.getProperty(arg + ".suffix");
String sql = prop.getProperty(arg + ".sql");
ci.setUrl(url);
ci.setDriver(driver);
ci.setPassword(password);
ci.setSql(sql);
ci.setSuffix(suffix);
ci.setUser(user);
tableList.put(arg, ci);
}
Now I am passing this tableList map to various threads like this and it won't be modified (by making set calls) by any thread. Each thread will be using get method to get the required method.
for (int i = 0; i< 1000; i++) {
service.submit(new Task(tableList));
}
So in the run method I need to make different conenctions basis on the tableList size. So if tableList size is two, that means I need to make two different connections, callableStatements and methods to two different database.
Question:-
So is there any better way as compared to way I am doing in my run method to create different connections to database basis on tableList size ?
Below is my Task class that impelements Runnable Interface
class Task implements Runnable {
private Connection[] dbConnection = null;
private CallableStatement[] callableStatement = null;
private ArrayList<Method> methods[] = null;
private final HashMap<String, TableConnectionInfo> tableLists;
public Task(HashMap<String, TableConnectionInfo> tableList) {
this.tableLists = tableList;
}
#Override
public void run() {
try {
int j = 0;
dbConnection = new Connection[tableLists.size()];
callableStatement = new CallableStatement[tableLists.size()];
methods = new ArrayList[tableLists.size()];
for (TableConnectionInfo ci : tableLists.values()) {
dbConnection[j] = getDBConnection(ci.getUrl(), ci.getUser(), ci.getPassword(), ci.getDriver());
callableStatement[j] = dbConnection[j].prepareCall(ci.getSql());
methods[j] = getRequiredMethods(ci.getSuffix());
j++;
}
}
}
}
Make a connection to that database-
private Connection getDBConnection(String url, String username, String password, String driver) {
Connection dbConnection = null;
try {
Class.forName(driver);
dbConnection = DriverManager.getConnection(url, username, password);
}
return dbConnection;
}
Just to add here getRequiredMethods will get all the methodNames of a particular table. So suppose if tableList size is 1 then we will be having only one connection to that database so getRequiredMethods will get all the methods for that table1 and store it in a ArrayList. But suppose if tableList size is 2 then we will be having two different connections to two different database so that is the reason I made methods as an array so that it can hold methods for table 1 and methods for table 2.
Ok, I'm still not sure how the Task is meant to use the data it's getting. But, I would move the getConnection, getCallableStatement and getMethods() functions to methods on the TableConnectionInfo. You can simply create a Collection of TableConnectionInfo (initialised as you already have, storing in an ArrayList). Then the Runnable simply iterates through the TableConnectionInfo.
public class TableConnectionInfo {
private String url;
private String user;
private String password;
private String driver;
private String suffix;
private String sql;
private Connection connection;
<snip... getters and setters for the properties>
public Connection getConnection() {
// TODO create and return a connection
if (connection == null) {
// create the connection
}
return connection;
}
public CallableStatement getCallableStatement() {
// get the callable statement
return null;
}
public Collection<Method> getMethods() {
// Get the Methods
return null;
}
}
public class TableTask implements Runnable {
private Collection<TableConnectionInfo> tables;
public TableTask(Collection<TableConnectionInfo> tables) {
this.tables = tables;
}
#Override
public void run() {
for (TableConnectionInfo table : tables) {
// do something with table.getConnection(), or table.getCallableStatement()
// and/or table.getMethods()
}
}
}