How to increase Testcoverage for Class that interacts with a Database? - java

I got a Java class called PatientRepositoryImpl, which contains some methods, that insert, delete or update Data in a MySql Database.
I've also written some Unit Tests for this class.
When i check the Coverage of my tests, i only get 59%, although almost every line of Code is marked green by the Coverage tool, except the SQL Exceptions.
I am new here and hope i did everything right, would be very thankful if someone could help me.
Here the code for my Repository and the Tests.
public class PatientRepositoryMySqlImpl implements PatientRepository {
private DatabaseConnection connection;
private PatientGenerator patientGenerator;
public PatientRepositoryMySqlImpl(DatabaseConnection connection, PatientGenerator patientGenerator) {
this.connection = connection;
this.patientGenerator = patientGenerator;
}
/* (non-Javadoc)
* #see com.id.hl7sim.PatientRepository#insertPatient()
*/
#Override
public void insertPatient(Patient patient) {
if (!patient.isValid()) {
throw new IllegalArgumentException("Incomplete Patient");
} else {
String insert = "INSERT INTO tbl_patient(lastname, firstname, gender, birthday) VALUES('"
+ patient.getLastname() + "', '" + patient.getFirstname() + "', '" + patient.getGender() + "', '"
+ patient.getBirthday().toString() + "');";
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.prepareStatement(insert)) {
statement.executeUpdate(insert);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}
/* (non-Javadoc)
* #see com.id.hl7sim.PatientRepository#insertListOfPatients()
*/
#Override
public void insertListOfPatients(List<Patient> allPatients) {
for (Patient patient : allPatients) {
insertPatient(patient);
}
}
/* (non-Javadoc)
* #see com.id.hl7sim.PatientRepository#getRandomPatient()
*/
#Override
public Patient getRandomPatient() {
Patient patient = new Patient.Builder().build();
String query = "SELECT * FROM tbl_patient ORDER BY RAND() LIMIT 1";
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.createStatement();) {
ResultSet rs = statement.executeQuery(query);
rs.next();
setPatientBasicData(patient, rs);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return patient;
}
private void setPatientBasicData(Patient patient, ResultSet rs) {
try {
patient.setId(rs.getInt("id"));
patient.setLastname(rs.getString("lastname"));
patient.setFirstname(rs.getString("firstname"));
patient.setGender(rs.getString("gender"));
patient.setBirthday(parseBirthday(rs.getString("birthday")));
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
public LocalDate parseBirthday(String birthday) {
LocalDate localDate = LocalDate.parse(birthday);
return localDate;
}
/* (non-Javadoc)
* #see com.id.hl7sim.PatientRepository#admitRandomPatient()
*/
#Override
public Patient admitRandomPatient() {
Patient patient = getRandomPatient();
patient.setDepartment(patientGenerator.getRandomDepartment());
patient.setWard(patientGenerator.getRandomWard());
patient.setAdmissionDateTime(LocalDateTime.now());
patient.setStatus("I");
String insert = "INSERT INTO tbl_inpatients(id, ward, department, admissionDate, patientStatus) VALUES('"
+ patient.getId() + "', '" + patient.getWard() + "', '" + patient.getDepartment() + "', '"
+ patient.getAdmissionDateTime().toString() + "', '" + patient.getStatus() + "')";
try (Connection dbConnection = connection.getDBConnection();
PreparedStatement statement = dbConnection.prepareStatement(insert)) {
statement.executeUpdate(insert, Statement.RETURN_GENERATED_KEYS);
ResultSet keys = statement.getGeneratedKeys();
keys.next();
patient.setCaseId(keys.getInt(1));
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return patient;
}
public Patient getRandomInpatient() {
Patient patient = new Patient.Builder().build();
String query = "SELECT * FROM tbl_inpatients ip, tbl_patient p WHERE p.id = ip.id ORDER BY RAND() LIMIT 1";
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.createStatement();) {
ResultSet rs = statement.executeQuery(query);
rs.next();
setPatientBasicData(patient, rs);
setPatientCaseData(patient, rs);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return patient;
}
public void setPatientCaseData(Patient patient, ResultSet rs) {
try {
patient.setWard(rs.getString("ward"));
patient.setDepartment(rs.getString("department"));
patient.setAdmissionDateTime(parseLocalDateTime(rs.getString("admissionDate")));
patient.setStatus(rs.getString("patientStatus"));
patient.setCaseId(rs.getInt("case"));
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
public LocalDateTime parseLocalDateTime(String localdatetime) {
localdatetime = localdatetime.replace("T", "");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss.SSS");
LocalDateTime formattedLocalDateTime = LocalDateTime.parse(localdatetime, formatter);
return formattedLocalDateTime;
}
/* (non-Javadoc)
* #see com.id.hl7sim.PatientRepository#transferRandomPatient()
*/
#Override
public Patient transferRandomPatient() {
Patient patient = getRandomInpatient();
patient.setPriorWard(patient.getWard());
patient.setPriorDepartment(patient.getPriorDepartment());
patient.setDepartment(patientGenerator.getRandomDepartment());
patient.setWard(patientGenerator.getRandomWard());
String update = "UPDATE tbl_inpatients SET ward='" + patient.getWard() + "', department='"
+ patient.getDepartment() + "' WHERE id='" + patient.getId() + "'";
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.prepareStatement(update)) {
statement.executeUpdate(update);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return patient;
}
/* (non-Javadoc)
* #see com.id.hl7sim.PatientRepository#dischargeRandomPatient()
*/
#Override
public Patient dischargeRandomPatient() {
Patient patient = getRandomInpatient();
patient.setDischargeDateTime(LocalDateTime.now());
insertFormerPatient(patient);
String delete = "DELETE FROM tbl_inpatients WHERE `case`=" + patient.getCaseId();
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.prepareStatement(delete)) {
statement.executeUpdate(delete);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return patient;
}
public void insertFormerPatient(Patient patient) {
String insert = "INSERT INTO tbl_formerpatients(`case`, `id`, ward, department, admissionDate, dischargeDate) VALUES('"
+ patient.getCaseId() + "', '" + patient.getId() + "', '" + patient.getWard() + "', '"
+ patient.getDepartment() + "', '" + patient.getAdmissionDateTime().toString() + "', '"
+ patient.getDischargeDateTime().toString() + "')";
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.prepareStatement(insert)) {
statement.executeUpdate(insert);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
public int countInpatients() {
int numberOfPatients = 0;
String query = "SELECT COUNT(id) AS numberOfPatients FROM tbl_inpatients";
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.createStatement();) {
ResultSet rs = statement.executeQuery(query);
while (rs.next()) {
numberOfPatients = rs.getInt(1);
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return numberOfPatients;
}
public int countPatients() {
int numberOfPatients = 0;
String query = "SELECT COUNT(id) AS numberOfPatients FROM tbl_patient";
try (Connection dbConnection = connection.getDBConnection();
Statement statement = dbConnection.createStatement();) {
ResultSet rs = statement.executeQuery(query);
while (rs.next()) {
numberOfPatients = rs.getInt(1);
}
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return numberOfPatients;
}
Tests
public class PatientRepositoryMySqlImplTest {
PatientRepository testPatientRepository;
Patient testPatient;
Patient testPatientTwo;
List<Patient> testBothPatients;
DatabaseConnection testConnection;
PatientGenerator testPatientGenerator;
Firstnames testFirstnames;
Lastnames testLastnames;
Departments testDepartments;
Wards testWards;
#Before
public void setUp() throws Exception {
testDepartments = JAXB.unmarshal(ClassLoader.getSystemResource("departments.xml"), Departments.class);
testWards = JAXB.unmarshal(ClassLoader.getSystemResource("wards.xml"), Wards.class);
testLastnames = JAXB.unmarshal(ClassLoader.getSystemResource("lastnames.xml"), Lastnames.class);
testFirstnames = JAXB.unmarshal(ClassLoader.getSystemResource("firstnames.xml"), Firstnames.class);
testPatientGenerator = new PatientGeneratorImpl(testFirstnames, testLastnames, testDepartments, testWards);
testPatient = testPatientGenerator.randomizeNewPatient();
testPatientTwo = testPatientGenerator.randomizeNewPatient();
testBothPatients = new ArrayList<Patient>();
testConnection = new MySqlConnection();
testPatientRepository = new PatientRepositoryMySqlImpl(testConnection, testPatientGenerator);
testPatientRepository.admitRandomPatient();
}
#Test
public void testAdmitRandomPatient() {
testPatient = testPatientRepository.admitRandomPatient();
assertTrue(testPatient.isValid());
}
#Test
public void testGetRandomInpatient() {
testPatient = testPatientRepository.getRandomInpatient();
assertTrue(testPatient.isValid());
}
#Test
public void testDischargeRandomPatientValid() {
testPatient = testPatientRepository.dischargeRandomPatient();
assertTrue(testPatient.isValid());
assertTrue(testPatient.getCaseId() != 0);
}
#Test
public void testDischargeRandomPatientDatabase() {
int beforeDischarge = testPatientRepository.countInpatients();
testPatient = testPatientRepository.dischargeRandomPatient();
int afterDischarge = testPatientRepository.countInpatients();
assertTrue(afterDischarge == beforeDischarge - 1);
}
#Test(expected = IllegalArgumentException.class)
public void testInsertPatientWitIncompletePatient() {
testPatient.setFirstname("");
testPatientRepository.insertPatient(testPatient);
}
#Test
public void testTransferRandomPatient() {
testPatient = testPatientRepository.transferRandomPatient();
assertTrue(testPatient.getDepartment() != testPatient.getPriorDepartment());
}
#Test
public void testInsertPatient() {
int numberOfPatients = testPatientRepository.countInpatients();
testPatientRepository.insertPatient(testPatient);
assertTrue(testPatientRepository.countInpatients() >= numberOfPatients);
}
#Test
public void testInsertListOfPatients() {
testBothPatients.add(testPatient);
testBothPatients.add(testPatientTwo);
int countInpatientsBeforeInsertion = testPatientRepository.countPatients();
testPatientRepository.insertListOfPatients(testBothPatients);
int countInpatientsAfterInsertion = testPatientRepository.countPatients();
assertTrue(countInpatientsAfterInsertion > countInpatientsBeforeInsertion);
}
Edit:
#Test
public void mockThrowsException() {
PatientRepository testPatientRepository = mock(PatientRepositoryMySqlImpl.class);
when(testPatientRepository.getRandomPatient()).thenThrow(SQLException.class);
testPatientRepository.admitRandomPatient();
}

While I completely agree that in order to increase the coverage you should definitely "simulate" the scenario that something goes wrong and SQLExceptions are thrown, let me introduce another approach that will also answer the question but hopefully give you another perspective.
JDBC is pretty cumbersome, and the tests that will throw SQL exceptions will probably not be the most pleasant tests to write. In addition, I see that you don't really deal with exceptions, and just log them in a console.
So maybe instead of trying to struggle with this, maybe you should consider to not work directly with JDBC but use some library that will wrap the hassle of JDBC usage for you. For example of such a library, take a look onto Spring JDBC Template, I know it's a pretty old stuff, but hey, working directly with JDBC is also probably not the most modern approach, so I'm trying to make fewer changes but still gain a value. Moreover one may say that its old and not fancy, I would say, a battle-tested library, that can be even without Spring itself.
Now since it wraps the JDBC Exception handling, among other things, the point is that you won't have to cover these cases at all. So your coverage will increase naturally.
Of course other low level and no-so-low level alternatives exist (like JDBI, or JOOQ to name a few), but this a different story, all of them will increase the coverage in a sense of a question you've asked.

Related

What is the right way to deal with the PreparedStatement in the Java program flow?

There are two methods in which the PreparedStatement is used.
The first method is called in the second method.
First method:
protected List<String> findResultsByMandantId(Long mandantId) {
List<String> resultIds = new ArrayList<>();
ResultSet rs;
String sql = "SELECT result_id FROM results WHERE mandant_id = ?";
PreparedStatement statement = getPreparedStatement(sql, false);
try {
statement.setLong(1, mandantId);
statement.execute();
rs = statement.getResultSet();
while (rs.next()) {
resultIds.add(rs.getString(1));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return resultIds;
}
Second method:
protected void findResultLineEntityToDelete(Long mandantId, String title, String context) {
List<String> resultIds = findResultsByMandantId(mandantId);
String [] resultIdsArr = resultIds.toArray(String[]::new);
ResultSet rs;
//String sql = "SELECT * FROM resultline WHERE result_id in (SELECT result_id FROM results WHERE mandant_id =" + mandantId + ")";
String sql = "SELECT * FROM resultline WHERE result_id in (" + String.join(", ", resultIdsArr)+ ")";
PreparedStatement statement = getPreparedStatement(sql, false);
try {
statement.execute();
rs = statement.getResultSet();
while (rs.next()) {
if (rs.getString(3).equals(title) && rs.getString(4).equals(context)) {
System.out.println("Titel: " + rs.getString(3) + " " + "Context: " + rs.getString(4));
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
The class in which both methods are located extends the JDBCBaseManager.
JDBCBaseManager:
private final String url = "jdbc:mysql://localhost:3306/database";
private final String userName = "root";
private final String password = "";
private Connection connection = null;
private PreparedStatement preparedStatement = null;
private int batchSize = 0;
public JDBCBaseManager() {
// Dotenv env = Dotenv.configure().directory("./serverless").load();
// url = env.get("DB_PROD_URL");
// userName = env.get("DB_USER");
// password = env.get("DB_PW");
}
public void getConnection() {
try {
if (connection == null) {
connection = DriverManager.getConnection(url, userName, password);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public PreparedStatement getPreparedStatement(String sql, boolean returnGeneratedKeys) {
try {
if (connection == null) {
getConnection();
}
if (preparedStatement == null) {
if (!returnGeneratedKeys) {
preparedStatement = connection.prepareStatement(sql);
} else {
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
connection.setAutoCommit(false);
}
}
return preparedStatement;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void closeConnection() {
try {
if (connection != null && !connection.isClosed()) {
System.out.println("Closing Database Connection");
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void startBatch(int batchSize) throws SQLException {
connection.setAutoCommit(false);
setBatchSize(batchSize);
}
public void commit() {
try {
if (connection != null && !connection.isClosed()) {
connection.commit();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public int getBatchSize() {
return batchSize;
}
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
The ResultSet in the second method still contains the results from the first method.
I already tried to close the connection and open it again before the second method is executed, but then I get the errors:
java.sql.SQLException: No operations allowed after statement closed.
java.sql.SQLNonTransientConnectionException: No operations allowed
after connection closed.
Can you tell me how to deal with the statement correctly in this case? Is my BaseManager incorrectly structured?
Here lies the error
public JDBCBaseManager() {
private PreparedStatement preparedStatement = null;
public PreparedStatement getPreparedStatement(String sql, boolean returnGeneratedKeys) {
try {
......
if (preparedStatement == null) {
if (!returnGeneratedKeys) {
preparedStatement = connection.prepareStatement(sql);
} else {
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
connection.setAutoCommit(false);
}
}
return preparedStatement;
You build the prepare statement only the first time the method getPreparedStatement is called because only the first time the field preparedStatement is null. Every next time you call the method getPreparedStatement you receive the previous preparedStatement from the previous SQL and not the new one.
Remove the check for if (preparedStatement == null) {
You need to build a new preparedStatement every time you want to execute a new SQL.

Is the MySQL procedure in this Minecraft plugin correct?

To get an idea of what the basic structure looks like, I downloaded a money system including MySQL from Spigot and looked at the code.
public static boolean playerExists(String uuid) {
try {
ResultSet rs = Simplecoinsystem.mysql.query("SELECT * FROM CoinData WHERE UUID= '" + uuid + "'");
if (rs.next())
return (rs.getString("UUID") != null);
return false;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static void createPlayer(String uuid) {
if (!playerExists(uuid))
Simplecoinsystem.mysql.update("INSERT INTO CoinData (UUID, COINS) VALUES ('" + uuid +
"', '" + Simplecoinsystem.getInstance().getConfig().getInt("startcoins") + "');");
}
public static Integer getCoins(String uuid) {
Integer i = Integer.valueOf(0);
if (playerExists(uuid)) {
try {
ResultSet rs = Simplecoinsystem.mysql.query("SELECT * FROM CoinData WHERE UUID= '" + uuid + "'");
if (rs.next())
Integer.valueOf(rs.getInt("COINS"));
i = Integer.valueOf(rs.getInt("COINS"));
} catch (SQLException e) {
e.printStackTrace();
}
} else {
createPlayer(uuid);
}
return i;
}
public static void setCoins(String uuid, Integer coins) {
if (playerExists(uuid)) {
Simplecoinsystem.mysql.update("UPDATE CoinData SET COINS= '" + coins + "' WHERE UUID= '" + uuid + "';");
} else {
createPlayer(uuid);
}
}
Am I correct that it is actually impractical to create a new entry with the uuid of the non-existent player after each query of the coins if the player does not exist?
Wouldn't this make it possible to flood the database with thousands of unnecessary entries by issuing, for example, a "/money (player)" command as an evil player/admin?
Couldn't I just ask when entering the server if the uuid is already stored and if not, just enter it? This way there would only be entries from players who have already been on the server before. Whether this needs great server performance, I'm not sure.
This is my first own MySQL class.
public class MySQL {
private String host, database, user, password;
private int port;
private Connection con;
public MySQL(String host, int port, String database, String user, String password) {
this.host = host;
this.port = port;
this.database = database;
this.user = user;
this.password = password;
connect();
}
public void connect() {
try {
con = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database + "?autoReconnect=true", user, password);
System.out.println("&cDie MySQL Verbindung wurde erfolgreich aufgebaut!");
} catch (SQLException e) {
e.printStackTrace();
}
}
public void disconnect() {
try {
if(this.con != null) {
this.con.close();
System.out.println("§cDie MySQL Verbindung wurde erfolgreich beendet!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void update(String query) {
try {
Statement st = con.createStatement();
st.executeUpdate(query);
st.close();
} catch (Exception e) {
e.printStackTrace();
connect();
}
}
public ResultSet qry(String query) {
ResultSet rs = null;
try {
Statement st = con.createStatement();
rs = st.executeQuery(query);
} catch (Exception e) {
e.printStackTrace();
connect();
}
return rs;
}
public Connection getConnection() {
return this.con;
}
}
Except for this part, both MySQL classes are built relatively the same.
This is the part that is in the MySQL class of the Spigot plugin.
Your code have multiple issues.
When the connection will be closed, next time you will have an error. In your Mysql class, I suggest you to do:
public Connection getConnection() {
if(con == null || con.isClosed())
connect();
return con;
}
Then, use it in all method like getConnection().prepareStatement().
You can be attacked with SQL Injection. To fix this, try to do something like:
PreparedStatement st = con.prepareStatement("SELECT * FROM CoinData WHERE UUID = ?");
st.setString(1, uuid.toString()); // Yes it start at 1 !!
st.executeUpdate();
With this, even with all values, you can't be attacked with injections.
You will have an error while getting coins:
if (rs.next()) // go to good line
Integer.valueOf(rs.getInt("COINS")); // useless convertion
i = Integer.valueOf(rs.getInt("COINS")); // error if no line.
You can just do:
if(rs.next())
i = rs.getInt("COINS");
If the column "UUID" is unique, you will not have duplicated lines.
Finally, about performance, it's better to do it one time: at login, instead of all time. You can also create an object stored in an hashmap to easier access to it, without using SQL, like that:
public static HashMap<UUID, Integer> coinsByPlayer = new HashMap<>();
OR:
public static HashMap<UUID, MyObject> coinsByPlayer = new HashMap<>();
public class MyObject {
private int coins = 0;
public MyObject(UUID uuid) {
// make SQL request to get data
}
public int getCoins() {
return coins;
}
public void setCoins(int next){
coins = next;
// here make "UPDATE" sql query
}
}
What do you say? Is it ok with the try/catch function? #Elikill58
public Connection getConnection() {
try {
if(con == null || con.isClosed()) {
connect();
}
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
edit:
public Connection getConnection_one() throws SQLException {
if(con == null || con.isClosed()) {
connect();
return con;
} else {
return con;
}
}

JDBC: can't send parameters from client to Derby DB by using servlet and Apache Tomcat

I'm try to send the parameters from servlet to db and I get errors in my connection.
Steps of my works:
In eclipse I create a Dynamic Web Project with Tomcat server
Create a model (without any technology as a Spring , Hibernate, JSP, JSF and so on. ) for to use with DML methodology (clearing java code).
I tested the created module (by using some main test class) and it works so good (connections , inserting, deleting..).
After that , I can a simple HTML doc for client side.
process for running a web module step by step:
Before start Web project I start to run Derby DB - works OK.
Starting a Web project with Apache Tomcat.
insert data to the HTML and submitted it. - works OK
In the Servlet class the data gated to the doGet(...) method. - works OK.
From the Servlet (doGet(...) method) when I try to send the parameter to the DB I get errors in my conector class.
How can I set right that?
Thanks.
Codes:
Servlet Class:
#WebServlet({ "/StudentServlet", "/Student" })
public class StudentServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private StudentDbDao sf = new StudentDbDao();
private Student student = new Student();
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter writer = response.getWriter();
String studentId = request.getParameter("sudentId");
Long id = Long.parseLong(studentId);
String studentFullName = request.getParameter("studentFullName");
String studentGendre = request.getParameter("studentGendre");
String studentGrade = request.getParameter("studentGrade");
try {
student.setId(id);
student.setFullName(studentFullName);
student.setGender(studentGendre);
student.setGrade(studentGrade);
sf.createStudent(student);
writer.println("The student #" + id + " inserted.");
} catch (StudentSystemException e) {
e.printStackTrace();
}
}
}
class StudentDbDao that response for DML functionality:
package dao.Db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import bean.Student;
import bean.StudentSystemException;
import dao.StudentDao;
import dao.connector.ConnectionPool;
public class StudentDbDao implements StudentDao {
private Connection conn;
public StudentDbDao() {
}
#Override
public void createStudent(Student student) throws StudentSystemException {
conn = ConnectionPool.getInstance().getConnection();
String sql = "INSERT INTO Students(ID, FULLNAME, GENDER, GRADE) VALUES(?, ?, ?, ?)";
PreparedStatement pstmt;
try {
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, student.getId());
pstmt.setString(2, student.getFullName());
pstmt.setString(3, student.getGender());
pstmt.setString(4, student.getGrade());
pstmt.executeUpdate();
System.out.println(student.getFullName() + " created successfully");
} catch (SQLException e) {
throw new StudentSystemException("Failed!", e);
} finally {
ConnectionPool.getInstance().returnConnection(conn);
}
}
#Override
public void removeStudent(Student student) throws StudentSystemException {
conn = ConnectionPool.getInstance().getConnection();
String sql = "DELETE FROM Students WHERE ID = " + student.getId();
PreparedStatement pstmt;
try {
pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
System.out.println(student.getId() + " removed successfully.");
} catch (SQLException e) {
throw new StudentSystemException("Failed!", e);
} finally {
ConnectionPool.getInstance().returnConnection(conn);
}
}
#Override
public Student getStudentById(long id) throws StudentSystemException {
conn = ConnectionPool.getInstance().getConnection();
String sql = "SELECT * FROM Students WHERE ID = " + id;
Student student = new Student();
PreparedStatement pstmt;
ResultSet rs;
try {
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
student.setId(rs.getLong(1));
student.setFullName(rs.getString(2));
student.setGender(rs.getString(3));
student.setGrade(rs.getString(4));
}
// else {
// System.out.print("Student with PID #" + id + " not exists. ");
// }
return student;
} catch (SQLException e) {
throw new StudentSystemException("Failed!", e);
} finally {
ConnectionPool.getInstance().returnConnection(conn);
}
}
public long getMaxRows() throws StudentSystemException {
conn = ConnectionPool.getInstance().getConnection();
String sql = "SELECT COUNT(*) FROM Students";
PreparedStatement pstmt;
int count = 0;
try {
pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
rs.next();
count = rs.getInt(1);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
ConnectionPool.getInstance().returnConnection(conn);
}
return count;
}
}
class ConnectionPool where code is falls when Servlet try to set the parameters:
public class ConnectionPool {
// static final int MAX_CONS = 1;
private Connection myconn = null;
// private Set<Connection> connections = new HashSet<Connection>();
private static ConnectionPool instance = new ConnectionPool();
String url = "jdbc:derby://localhost:1527/StudentDB";
private ConnectionPool() {
try {
myconn = DriverManager.getConnection(url);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static ConnectionPool getInstance() {
return instance;
}
public Connection getConnection() {
// Connection conn = myconn;
return this.myconn;
}
public void returnConnection(Connection conn) {
this.myconn = conn;
// myconn.add(conn);
}
public void closeAllConnections() throws StudentSystemException {
Connection connection = myconn;
try {
connection.close();
} catch (SQLException e) {
throw new StudentSystemException("Failed to close connection: ", e);
}
}
attached before submit and after print-screens:
before:
after:

Java - How can I INSERT values in SQLite?

Im starting in SQLite, I created my db and the connection works.
When I try to Insert a value (the db is empty) I get the following error:
java.sql.SQLException: near ".684": syntax error
import java.sql.*;
public class connection{
String route = "C:\\Freeman SA.db";
Connection c = null;
Statement stat = null;
String op;
public void connect(){
try{
c = DriverManager.getConnection("jdbc:sqlite:"+route);
if (c!=null)
System.out.println("Connected to db.");
}
catch (SQLException ex) {
System.err.println("Couldn't connect."+ex.getMessage());
}
}
public void insert_DB(String NAME, String LNAME, String ID, int AGE, int TIME, int SALARY) throws SQLException{
connect();
try {
stat = c.createStatement();
op = "INSERT INTO Remuneraciones (Nombre, Apellido, Rut, Edad, Tiempo, Sueldo) VALUES (" + NAME + ", " + LNAME + ", " + ID + ", " + AGE + ", " + TIME + ", " + SALARY + ");";
stat.executeUpdate(op); //Here is the problem
stat.close();
}
catch (SQLException e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
}
c.close();
}
}
Main.
public static void main(String[] args) throws IOException {
connection n = new connection();
try {
n.insert_DB("Charlie", "White", "18.954.621-K", 21, 2, 650000);
} catch (SQLException ex) {
Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
}
}
PD: I'm learning from here: http://www.tutorialspoint.com/sqlite/sqlite_java.htm
It's a bad idea to create a SQL statement by concatenating Strings like that. Do some research into SQL injection attack and Little Bobby Tables.
PreparedStatement is a better idea. Bind your variables after validation.
See if this makes your life better:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Demo RenumerationDao
* Created by Michael
* Creation date 6/8/2016.
* #link https://stackoverflow.com/questions/37714254/java-how-can-i-insert-values-in-sqlite/37714292#37714292
*/
public class RenumerationDao {
private static final String INSERT_SQL = "INSERT INTO Remuneraciones(Nombre, Apellido, Rut, Edad, Tiempo, Sueldo) VALUES(?, ?, ?, ?, ?, ?)";
private Connection connection; // Better to get this from a pool.
public RenumerationDao(Connection connection) {
this.connection = connection;
}
public int insert(String firstName, String lastName, String id, int age, int timeInHours, int salary) {
int numRowsInserted = 0;
PreparedStatement ps = null;
try {
ps = this.connection.prepareStatement(INSERT_SQL);
ps.setString(1, firstName);
ps.setString(2, lastName);
ps.setString(3, id);
ps.setInt(4, timeInHours);
ps.setInt(5, age); // You'll have to update this each and every year. BirthDate would be better.
ps.setInt(6, salary);
numRowsInserted = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(ps);
}
return numRowsInserted;
}
public static void close(Statement statement) {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

How to retrieve sequences metadata from JDBC?

I am trying to retrieve different kind of metadata of my Oracle DB from Java code (using basic JDBC). For example, if I want to retrieve the list of tables with _FOO suffix, I can do something like:
Connection connection = dataSource.getConnection();
DatabaseMetaData meta = connection.getMetaData();
ResultSet tables = meta.getTables(connection.getCatalog(), null, "%_FOO", new String[] { "TABLE" });
// Iterate on the ResultSet to get information on tables...
Now, I want to retrieve all the sequences from my database (for example all sequence named S_xxx_FOO).
How would I do that, as I don't see anything in DatabaseMetaData related to sequences?
Do I have to run a query like select * from user_sequences ?
Had the same question. It's fairly easy. Just pass in "SEQUENCE" into the getMetaData().getTables() types param.
In your specific case it would be something like:
meta.getTables(connection.getCatalog(), null, "%_FOO", new String[] { "SEQUENCE" });
You can't do this through the JDBC API, because some databases (still) do not support sequences.
The only way to get them is to query the system catalog of your DBMS (I guess it's Oracle in your case as you mention user_sequences)
You can use the hibernate dialect api for retrieving sequence Name. see : http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/dialect/Dialect.html
From below example, you can see how to use dialect to get sequence names
public static void main(String[] args) {
Connection jdbcConnection = null;
try {
jdbcConnection = DriverManager.getConnection("", "", "");
printAllSequenceName(jdbcConnection);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(jdbcConnection != null) {
try {
jdbcConnection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void printAllSequenceName(Connection conn) throws JDBCConnectionException, SQLException {
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(conn.getMetaData());
if ( dialect.supportsSequences() ) {
String sql = dialect.getQuerySequencesString();
if (sql!=null) {
Statement statement = null;
ResultSet rs = null;
try {
statement = conn.createStatement();
rs = statement.executeQuery(sql);
while ( rs.next() ) {
System.out.println("Sequence Name : " + rs.getString(1));
}
}
finally {
if (rs!=null) rs.close();
if (statement!=null) statement.close();
}
}
}
}
If you don't desire to use hibernate, then you have to crate custom sequential specific implementation.
Sample code for custom implementation
interface SequenceQueryGenerator {
String getSelectSequenceNextValString(String sequenceName);
String getCreateSequenceString(String sequenceName, int initialValue, int incrementSize);
String getDropSequenceStrings(String sequenceName);
String getQuerySequencesString();
}
class OracleSequenceQueryGenerator implements SequenceQueryGenerator {
#Override
public String getSelectSequenceNextValString(String sequenceName) {
return "select " + getSelectSequenceNextValString( sequenceName ) + " from dual";
}
#Override
public String getCreateSequenceString(String sequenceName,
int initialValue, int incrementSize) {
return "create sequence " + sequenceName + " start with " + initialValue + " increment by " + incrementSize;
}
#Override
public String getDropSequenceStrings(String sequenceName) {
return "drop sequence " + sequenceName;
}
#Override
public String getQuerySequencesString() {
return "select sequence_name from user_sequences";
}
}
class PostgresSequenceQueryGenerator implements SequenceQueryGenerator {
#Override
public String getSelectSequenceNextValString(String sequenceName) {
return "select " + getSelectSequenceNextValString( sequenceName );
}
#Override
public String getCreateSequenceString(String sequenceName,
int initialValue, int incrementSize) {
return "create sequence " + sequenceName + " start " + initialValue + " increment " + incrementSize;
}
#Override
public String getDropSequenceStrings(String sequenceName) {
return "drop sequence " + sequenceName;
}
#Override
public String getQuerySequencesString() {
return "select relname from pg_class where relkind='S'";
}
}
public void printSequenceName (SequenceQueryGenerator queryGenerator, Connection conn) throws SQLException {
String sql = queryGenerator.getQuerySequencesString();
if (sql!=null) {
Statement statement = null;
ResultSet rs = null;
try {
statement = conn.createStatement();
rs = statement.executeQuery(sql);
while ( rs.next() ) {
System.out.println("Sequence Name : " + rs.getString(1));
}
}
finally {
if (rs!=null) rs.close();
if (statement!=null) statement.close();
}
}
}
public static void main(String[] args) {
Connection jdbcConnection = null;
try {
jdbcConnection = DriverManager.getConnection("", "", "");
printAllSequenceName(new OracleSequenceQueryGenerator(), jdbcConnection);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(jdbcConnection != null) {
try {
jdbcConnection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Given that recent versions of the Oracle JDBC drivers (e.g. 12.1.0.2) don't return sequence information when you call DatabaseMetaData#getTables with types set to ["SEQUENCE"], your best bet is to run the necessary query yourself, e.g.:
SELECT o.owner AS sequence_owner,
o.object_name AS sequence_name
FROM all_objects o
WHERE o.owner LIKE 'someOwnerPattern' ESCAPE '/'
AND o.object_name LIKE 'someNamePattern' ESCAPE '/'
AND o.object_type = 'SEQUENCE'
ORDER BY 1, 2
... where someOwnerPattern and someNamePattern are SQL patterns like the ones you'd use with the LIKE operator (e.g. % matches anything).
This is basically the same as the query run by the driver itself, except that it queries for objects of type SEQUENCE.

Categories