I had a class for a db connection like that (simplified):
public class SQL {
private static SQL instance = null;
private static String ADRESS;
private SQL() {
try {
// Settings
settings = Settings.getInstance();
ADDRESS = settings.getAdress;
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://" + ADRESS + "...");
}
catch (SQLException e) {
throw e;
}
}
public static SQL getInstance() {
if(instance == null) {
instance = new SQL();
}
return instance;
}
public String select(...) {
// SELECT Code
}
public String update(...) {
// UPDATE Code
}
public String insert(...) {
// INSERT Code
}
}
But now I need two variants of SQL and they mainly differ only in the settings (different databases). So I wanted to make SQL to an abstract class and overwrite only the constructor in the two inherited classes.
But as far as I could find out it's not possible to have an abstract private constructor!?
So how can I change the class SQL to an abstract class then?
Thanks, I hope someone understands the problen :)
I would use something like this:
public static SQL getOrMakeInstance1() {
if(instance1 == null) {
instance1 = new SQL(pram11,param12,param13);
}
return instance1;
}
public static SQL getOrMakeInstance2() {
if(instance2 == null) {
instance2 = new SQL(pram21,param22,param23);
}
return instance2;
}
-maybe is a factory pattern, I don't know the names from design patterns :)
I hope it helps.
I'm not sure you need more than 1 class. What if you changed your class's constructor to take a Settings instance?
public SQL(Settings settings){
ADDRESS = settings.getAddress();
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://" + ADRESS + "...");
}
Then what you make different Setting instances
Settings settings = ...
SQL sql = new SQL(settings);
Settings differentSettings =...
SQL differentSql = new SQL(differentSettings);
Related
I'm currently doing a mini parking system for our JAVA bootcamp.
I'm trying to get the value of the integer variables into a different class.
These variables are results of the sql counts of total parked (countCar,countMotor, and countVan).
This is what I did:
I stored them in a class called SQLCountResult:
public class SQLConnections {
public Connection con = null;
public void parkInSQL() {
con = dbConnect.con();
int countCar;
int countMotor;
int countVan;
int parkCar;
int parkMotor;
int parkVan;
int[] vehicleTotal = new int[3];
String qCar = "SELECT COUNT(*) FROM `vehicle` WHERE `vType` = 1 AND `parkout` IS NULL";
String qMotor = "SELECT COUNT(*) FROM `vehicle` WHERE `vType` = 2 AND `parkout` IS NULL";
String qVan = "SELECT COUNT(*) FROM `vehicle` WHERE `vType` = 3 AND `parkout` IS NULL";
try {
Statement stmtCar = con.createStatement();
Statement stmtMotor = con.createStatement();
Statement stmtVan = con.createStatement();
ResultSet rsCar = stmtCar.executeQuery(qCar);
ResultSet rsMotor = stmtMotor.executeQuery(qMotor);
ResultSet rsVan = stmtVan.executeQuery(qVan);
rsCar.next();
rsMotor.next();
rsVan.next();
countCar = rsCar.getInt(1);
countMotor = rsMotor.getInt(1);
countVan = rsVan.getInt(1);
} catch(SQLException e) {
e.printStackTrace();
}
}
}
Now I want to call countCar, countMotor, and countVan to another class called Park. My code for the Park class is below:
public class Park {
public Connection con = null;
public void parkMethod() {
SQLConnections sqlConnections = new SQLConnections();
sqlConnections.parkInSQL();
}
}
I also tried using extend but this didn't work either. How can I call these variables to the Park class?
I suggest you first do some supplementary reading on class members and using objects. If you are not familiar with objects, read this comprehensive article called What Is an Object?. I cannot stress how important it is to understand this. The very concept of "Object Oriented Programming" is centered around objects. Once you have familiarized yourself with this, then go on and read the rest of my answer.
Now that you are familiar with the articles I cited above, I will begin by setting up some fields, which are explained in the "Declaring Member Variables" article. First we need ask ourselves, "Which variables are we trying to retrieve in our Park class?" As you mentioned in your question, the concerned variables are countCar, countMotor, and countVan.
We have identified the variables we are after. Now what? The next step is to change these variables to fields so that they don't get gobbled up by the garbage collector when your parkInSQL method returns. This is a simple task. Let's start by removing the variables from your parkInSQL method:
public void parkInSQL() {
con = dbConnect.con();
̶ ̶i̶n̶t̶ ̶c̶o̶u̶n̶t̶C̶a̶r̶;̶
̶ ̶i̶n̶t̶ ̶c̶o̶u̶n̶t̶M̶o̶t̶o̶r̶;̶
̶ ̶i̶n̶t̶ ̶c̶o̶u̶n̶t̶V̶a̶n̶;̶
//...
}
Next we need to declare these variables as fields. To do that, simply place the variable declarations at the top of your class, just like you did with your Connection variable:
public class SQLConnections {
public Connection con = null;
public int countCar;
public int countMotor;
public int countVan;
//...
}
Notice that I used the public access modifier. This is to make sure that these fields will be visible to our Park class (We could make them private, but that would get us into getters and setters, which is a lesson for another time).
Once we have our variables declared as fields, we should be all set with the SQLConnections class. The rest is easy as pie.
You already have half of the work done. If you look at your parkMethod, you'll see that you constructed an SQLConnections object:
SQLConnections sqlConnections = new SQLConnections();
Now all we need to do is reference the newly created fields, which is explained in the "using objects" article I cited above. After you call sqlConnections.parkInSQL(), you can reference the fields with sqlConnections.countCar, sqlConnections.countMotor, and sqlConnections.countVan:
public class Park {
public Connection con = null;
public void parkMethod() {
SQLConnections sqlConnections = new SQLConnections();
sqlConnections.parkInSQL();
int countCar = sqlConnections.countCar;
int countMotor = sqlConnections.countMotor;
int countVan = sqlConnections.countVan;
}
}
Now you can operate on these values accordingly.
You could change the scope of the variables.
Move this:
int countCar;
int countMotor;
int countVan;
outside of the parkInSQL method, and add the access modifier, public:
public int countCar;
public int countMotor;
public int countVan;
Then you can access it from the Park class like this:
public class Park {
public Connection con = null;
public void parkMethod() {
SQLConnections sqlConnections = new SQLConnections();
int iAmUsingCountCar = sqlConnections.countCar;
}
}
Variables defined inside a method are local to that method. If you want to share variables between class/methods, then you'll need to specify them as member variables of the class. You should initialise it outside the methods.
public class SQLConnections {
public int countCar;
public int countMotor;
public int countVan;
//...
}
public class VehicleStatistics {
private int countCar;
private int countMotor;
private int countVan;
public VehicleStatistics(int countCar, int countMotor, int countVan) {
this.countCar = countCar;
this.countMotor = countMotor;
this.countVan = countVan;
}
public int getCountCar() {
return countCar;
}
public void setCountCar(int countCar) {
this.countCar = countCar;
}
public int getCountMotor() {
return countMotor;
}
public void setCountMotor(int countMotor) {
this.countMotor = countMotor;
}
public int getCountVan() {
return countVan;
}
public void setCountVan(int countVan) {
this.countVan = countVan;
}
}
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class VehicalesDAOImpl {
private Connection connection;
public VehicalesDAOImpl(Connection connection) {
this.connection = connection;
}
public VehicleStatistics getVehicleStatistics() {
String qCar = "SELECT COUNT(*) FROM `vehicle` WHERE `vType` = 1 AND `parkout` IS NULL";
String qMotor = "SELECT COUNT(*) FROM `vehicle` WHERE `vType` = 2 AND `parkout` IS NULL";
String qVan = "SELECT COUNT(*) FROM `vehicle` WHERE `vType` = 3 AND `parkout` IS NULL";
VehicleStatistics result = null;
try {
Statement stmtCar = connection.createStatement();
Statement stmtMotor = connection.createStatement();
Statement stmtVan = connection.createStatement();
ResultSet rsCar = stmtCar.executeQuery(qCar);
ResultSet rsMotor = stmtMotor.executeQuery(qMotor);
ResultSet rsVan = stmtVan.executeQuery(qVan);
rsCar.next();
rsMotor.next();
rsVan.next();
int countCar =rsCar.getInt(1);
int countMotor =rsMotor.getInt(1);
int countVan =rsVan.getInt(1);
result = new VehicleStatistics(countCar, countMotor, countVan);
} catch(SQLException e) {
//log error
} finally {
//close connections etc...
}
return result;
}
}
I have a library which parse URLs and extract some data. There is one class per URL. To know which class should handle the URL provided by the user, I have the code below.
public class HostExtractorFactory {
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
switch (host) {
case HostExtractorABC.HOST_NAME:
return HostExtractorAbc.getInstance();
case HostExtractorDEF.HOST_NAME:
return HostExtractorDef.getInstance();
case HostExtractorGHI.HOST_NAME:
return HostExtractorGhi.getInstance();
default:
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
}
The problem is users are requesting more URL to be parsed, which means my switch statement is growing. Every time someone comes up with a parser, I have to modify my code to include it.
To end this, I've decided to create a map and expose it to them, so that when their class is written, they can register themselves to the factory, by providing the host name, and the extractor to the factory. Below is the factory with this idea implemented.
public class HostExtractorFactory {
private static final Map<String, HostExtractor> EXTRACTOR_MAPPING = new HashMap<>();
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
if(EXTRACTOR_MAPPING.containsKey(host)) {
return EXTRACTOR_MAPPING.get(host);
} else {
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
public static void register(String hostname, HostExtractor extractor) {
if(StringUtils.isBlank(hostname) == false && extractor != null) {
EXTRACTOR_MAPPING.put(hostname, extractor);
}
}
}
And the user would use it that way:
public class HostExtractorABC extends HostExtractor {
public final static String HOST_NAME = "www.abc.com";
private static class HostPageExtractorLoader {
private static final HostExtractorABC INSTANCE = new HostExtractorABC();
}
private HostExtractorABC() {
if (HostPageExtractorLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
HostExtractorFactory.register(HOST_NAME, this);
}
public static HostExtractorABC getInstance() {
return HostPageExtractorLoader.INSTANCE;
}
...
}
I was patting my own back when I realized this will never work: the user classes are not loaded when I receive the URL, only the factory, which means their constructor never runs, and the map is always empty. So I am back to the drawing board, but would like some ideas around getting this to work or another approach to get rid of this pesky switch statement.
S
Another option is to use the Service Loader approach.
Having your implementers add something like the following in ./resources/META-INF/services/your.package.HostExtractor:
their.package1.HostExtractorABC
their.package2.HostExtractorDEF
their.package3.HostExtractorGHI
...
Then in your code, you can have something like:
HostExtractorFactory() {
final ServiceLoader<HostExtractor> loader
= ServiceLoader.load(your.package.HostExtractor.class);
for (final HostExtractor registeredExtractor : loader) {
// TODO - Perform pre-processing which is required.
// Add to Map? Extract some information and store? Etc.
}
}
I would advice for you to learn about dependency injection (I love spring implementation). You will then be able to write an interface like
public interface HostExtractorHandler {
public String getName();
public HostExtractor getInstance();
}
Than your code can "ask" for all classes that implements this interface, you then would be able to build your map in the initialization phase of your class.
I would use the Reflections library to locate the parsers. They all appear to derive from the HostExtractor class, so use the library to locate all subtypes:
Reflections reflections = new Reflections("base.package");
Set<Class<? extends HostExtractor>> extractorTypes =
reflections.getSubTypesOf(HostExtractor.class);
Use the results to create the instances in your factory:
for (Class<? extends HostExtractor> c : extractorTypes) {
HostExtractor he = c.newInstance();
EXTRACTOR_MAPPING.put(he.getHostName(), he);
}
I made up the getHostName method, but it should be trivial to add to the HostExtractor base class.
Ok so here is my code:
class ConnectionTest implements Connection {
Random randomGenerator;
public String id;
public ConnectionTest() {
randomGenerator = new Random();
id = UUID.randomUUID().toString();
}
public boolean testConnection() {
if (randomGenerator.nextInt(10)<3) //randomly make some false.
return false;
return true;
}
}
class ConnectionFactoryTest implements ConnectionFactory {
public Connection newConnection() {
Connection c = new ConnectionTest();
if (c == null)
throw new ConnectionException("New connection failed.");
System.out.println("New connection: " + c.id);
return new ConnectionTest();
}
}
And the compiler complains that c doesn't have id. I have declared id as public, so shouldn't it be accessible by other classes?
The compiler checks at compile time the type of c's reference : and since it's a Connection reference (the Connection class has no id attribute), compilation fails.
This code should work:
ConnectionTest c = new ConnectionTest();
One workaround for Connection c = new ConnectionTest(); to work is to define the id member variable in the Connection class instead.
I want to display list of record from database to output text field. I'm having problem with method with brings record from database. It is causing an infinite loop as it call in constructor of managed bean class. Here is the code.
Constructor of managed bean class:
public InterViewDto() throws SQLException {
User u = getCurrentUser();
InterviewDao d = new InterviewDao();
List<InterViewDto> dao1 = d.getCall(u.getEmailAddress());
setDto(dao1);
}
Method bringing record from database :
public List<InterViewDto> getCall(String email) throws SQLException {
System.out.print("fyc");
List<InterViewDto> list = new ArrayList<InterViewDto>();
String job = null;
boolean exists = false;
Connection c = null;
try {
c = openConnection();
String query_check = "SELECT * FROM interviewcall WHERE useremail = '"+email+"' ";
Statement st = c.createStatement();
ResultSet rs = st.executeQuery(query_check);
while (rs.next()) {
InterViewDto dto = new InterViewDto();
dto.setDate( rs.getDate("time"));
dto.setJobtitle( rs.getString("jobtitle"));
dto.setJobtitle( rs.getString("useremail"));
list.add(dto);
System.out.print(list.get(0).getJobtitle());
} rs.close();
} catch (Exception e) {
System.out.println(e);
} finally {
c.close();
}
return list;
}
You have a circular dependency. Your constructor for the DTO reaches out to the database, which in turn creates a new DTO to represent the data loaded from the database, which goes to the database and back and forth until you overflow the call stack.
Quite simply, you have merged two complementary design approaches.
Either your InterViewDto constructor loads data from the DAO or the DAO constructs a new InterViewDto object. Pick one or the other.
In my opinion, it makes more sense for the DAO to create the DTO objects. If you want the DTO to delegate to the DAO as a matter of convenience, consider a static method.
public class InterViewDto {
public InterViewDto() {
}
...
public static fromCurrentUser() {
return new InterviewDao().getCall(getCurrentUser().getEmailAddress());
}
}
Then change your constructor to be empty.
This is my code.
private void btnloginActionPerformed(java.awt.event.ActionEvent evt) {
String username = "";
String sql = "select * from userinfo where uname=? and pword=?";
try{
pst = conn.prepareStatement(sql);
pst.setString(1, txt_username.getText());
pst.setString(2, txt_password.getText());
rs=pst.executeQuery();
if(rs.next()){
afterLogin pwcorrect = new afterLogin();
pwcorrect.setVisible(true);
this.setVisible(false);
username = txt_username.getText();
}
else{
JOptionPane.showMessageDialog(null, "Username and Password are Incorrect.");
}
}
catch(Exception e){
JOptionPane.showMessageDialog(null, e);
}
}
I need to access the value of username from another class. Is there any way for this??? Please help me with code.
That's not just defined in the class, it's defined in a method in a class.
This isn't a "private" variable (or class), it's an invisible variable as far as other classes are concerned. Expose it with a public getter, or provide a mechanism the code can set it on something else.
Since this is an action handler you also need to make sure the variable will only be accessed after it's been set, or that however it's accessed can deal with it not having a value.
public class WhateverClassThisIs {
private String username;
public String getUsername() { return username; }
private void btnloginActionPerformed(java.awt.event.ActionEvent evt) {
String sql = "select * from userinfo where uname=? and pword=?";
try {
// As before, but:
if (rs.next()) {
username = txt_username.getText();
}
}
}
}
public class SomeOtherClass {
private WhateverClassThisIs theOtherClass;
public void setTheOtherClass(WhateverClassThisIs theOtherClass) {
this.theOtherClass = theOtherClass;
}
public void whatever() {
String username = theOtherClass.getUsername();
}
}
The other mechanism would rely on passing something in to WhateverClassThisIs with a username setter, roughly:
public class WhateverClassThisIs {
private SomeOtherClass someOtherClass;
public WhateverClassThisIs(SomeOtherClass someOtherClass) {
this.someOtherClass = someOtherClass;
}
private void btnloginActionPerformed(java.awt.event.ActionEvent evt) {
String sql = "select * from userinfo where uname=? and pword=?";
try {
// As before, but:
if (rs.next()) {
someOtherClass.setUsername(txt_username.getText());
}
}
}
}
public class SomeOtherClass {
private String username;
public void setUsername(String username) {
this.username = username;
}
public void whatever() {
// Do something with username--but either try
// after you know it's been set, or by being
// able to handle it being null/empty/whatever
}
}
In addition, you could use an Observer, any of several Swing-ish mechanisms, and so on.
Yes there are ways to do it using reflection ... if the variable is a field.
But the best solution is change the other class so that it has accessible getter and/or setter methods ... after you have thought through all of the implications.
Breaking a classes encapsulation to access private state is something you should only do as a last resort.
If the variable is a local variable, then there is NO way that some other class or method can update it. And you can only access it from another class if that class is a locally declared anonymous inner class and the variable is declared as final. That doesn't sound like what you are trying to do here ....