Java - Oracle Database Change Notification - java

I am trying to implement a event listener which can identify DATABASE CHANGE NOTIFICATION (Oracle). According to the reference website, it said that event will triggle and print ROW_ID when something change in EXAMPLE table. I want this project running, and it should give me a message "give me something!" if I manually insert/update data in Database. However, it is my understanding that this code will terminate no matter what since there is no infinite loop that can be interrupted by the event. Please correct me if I am wrong.
Additional Question]
By setting OracleConnection.DCN_NOTIFY_ROWIDS as true, it will notify every event including inserting, updating, deleting. Am I correct? I was confused with the meaning of "Database change events will include row-level details, such as operation type and ROWID"
my code:
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleStatement;
import oracle.jdbc.dcn.DatabaseChangeEvent;
import oracle.jdbc.dcn.DatabaseChangeListener;
import oracle.jdbc.dcn.DatabaseChangeRegistration;
public class DBTest {
static final String USERNAME = "username";
static final String PASSWORD = "password";
static String URL = "jdbc:oracle:thin:#url:port/name";
public static void main(String[] args) {
DBTest oracleDCN = new DBTest();
try {
oracleDCN.run();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void run() throws Exception {
OracleConnection conn = connect();
Properties prop = new Properties();
prop.setProperty(OracleConnection.DCN_NOTIFY_ROWIDS, "true");
DatabaseChangeRegistration dcr = conn.registerDatabaseChangeNotification(prop);
try {
dcr.addListener(new DatabaseChangeListener() {
public void onDatabaseChangeNotification(DatabaseChangeEvent dce) {
System.out.println("GIVE ME SOMETHING!");
}
});
//conn.unregisterDatabaseChangeNotification(dcr);
Statement stmt = conn.createStatement();
((OracleStatement) stmt).setDatabaseChangeRegistration(dcr);
ResultSet rs = stmt.executeQuery("select * from Schema.T_TEST");
while (rs.next()) {
}
rs.close();
stmt.close();
} catch (SQLException ex) {
if (conn != null) {
conn.unregisterDatabaseChangeNotification(dcr);
conn.close();
}
throw ex;
}
}
OracleConnection connect() throws SQLException {
OracleDriver dr = new OracleDriver();
Properties prop = new Properties();
prop.setProperty("user", DBTest.USERNAME);
prop.setProperty("password", DBTest.PASSWORD);
return (OracleConnection) dr.connect(DBTest.URL, prop);
}
}
More details can be found in the reference website

As you guessed you need keep your main thread alive otherwise your program will exit. You can just make it sleep or do something more useful. Also you can close the JDBC connection but you don't want to close (unregister) the registration immediately. The way Database Change Notification works is with an internal listening thread that runs within the driver. This listening thread will receive outband events sent by the server through a dedicated network socket, process these events and notify the listeners. This listening thread will be closed if you unregister. When you're no longer interested in receiving these events you can create another connection to the database to unregister which will end up closing the driver's listening thread.

Related

How to fetch records from Oracle databse using Java

I'm learning database with Java using JDBC. I've created a svery basic program after reading some articles on JDBC on the internet. My code is not giving any error but I'm not getting the output also. Here's is my code:
This is my connectivity code: DataService
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DataService {
static Connection con;
public static void main(String args[]){
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con=DriverManager.getConnection(
"jdbc:oracle:thin:#localhost:1521:xe","system","scott");
}
catch(Exception e){
System.out.println(e);
}
}
public ResultSet getRecords() {
System.out.println("inside getRecords()");
ResultSet rs=null;
try {
System.out.println("Inside Rs loop");
Statement stmt=con.createStatement();
rs=stmt.executeQuery("select * from emp");
System.out.println("RS before entering while: "+rs);
while(rs.next())
System.out.println("Inside while loop");
System.out.println("Data: " +rs.getInt(0)+" "+rs.getString(1));
} catch (SQLException ex) {
Logger.getLogger(DataService.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Before sending: "+rs);
return rs;
}
}
And here's is my application MainFrame.java
public class MainFrame extends Application {
...
#Override
public void start(Stage primarystage) {
...
try {
System.out.println("Inside try");
DataService dataservice = new DataService();
System.out.println("Result set: "+dataservice.getRecords());
} catch (Exception e) {
//TODO: handle exception
}
}
}
I'm getting this output:
Inside try
inside getRecords()
Inside Rs loop
I cant see any output related to records. Also I've checked select * from emp. There are rows in that table already.
I'm missing out something very important here. Please correct me.
When you create DataService instance DataService dataservice = new DataService(); method main() is not called, therefore variable conn is not initialized and remains null. After that when calling Statement stmt=con.createStatement(); a NullPointerException (NPE) is thrown. This NPE is not catched neither in this catch block catch (SQLException ex) because it catches only SQLExceptions nor in this one catch (Exception e), because there is no handling logic provided.
First step in making code work is adding some exception handling logic in method start() of MainFrame class (never leave empty catch blocks, it's considered as an antipattern).
Then move logic for connection initialization either to getRecords() method making it local variable or to DataService constructor, so variable conn will be initialized, when accessing it.
Next big issue is closing Connection, Statement and ResultSet variables after finishing work with them. Since java 7 you can use try-with-resources syntax to handle such resources.
Also I would recommend to handle ResultSet inside of getRecords() method without exposing it outside DataService class.This class acts as Data Access Object (DAO) and it would be better to keep implementation details inside. Instead you could create some simple Employee class, which instances could be initialized inside while(rs.next()) loop, filled with values from result set and added to some collection. This collection could be returned from getRecords() method.
you should obtain connection instance in same thread as you use it
move connection creation to getRecords method

Creating Connection Pool In Standalone Java Application

I need to use a connection pool in a standalone (as in non-web) Java application. Where I work, we are not allowed to use APIs without going through layers of security, and the job needs to be completed soon. Below is my attempt at creating this connection pool.
I have unit tested this code and tested it within the context of the overall application a hundred times and in all cases the tests passed with zero errors, and in addition the performance of each run is just under three thousand times faster than a simple connect, retrieve data, disconnect in serial approach; however, I still have nagging concerns that there could be issues with this approach that I simply haven't unearthed yet. I would appreciate any advice anyone has concerning the below code. This is my first post on this site; please let me know if I've made any errors in etiquette. I did search this site about this problem before posting. Please see below the code for an invocation example. Thanks. --JR
package mypackage;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Note: This class is only instantiated once per application run.
* Multiple instantiations, as specified in the release notes,
* are not supported.
*/
public class ConnectionManager {
// Use a blocking queue to store the database connections.
// The application will only be called once, by a single user,
// but within the application many threads will require
// a connection.
private BlockingQueue<Connection> connectionQueue = null;
// Load the connection queue with a user-defined number of connections.
// Params contains a map of all non hard-coded variables in the
// application.
public ConnectionManager(int howMany, Map<String, Object> params) {
Database database = new Database();
connectionQueue = new ArrayBlockingQueue<Connection>(howMany);
for(int i = 0; i < howMany; i++) {
connectionQueue.add(database.getConn(params));
}
}
// Return a connection from the queue, waiting up to 15 minutes to do so.
// 15 minutes is hard-coded because it is the standard time-out for all
// processes at our agency. This application must complete in less
// than fifteen minutes (is currently completing in thirty five seconds).
public Connection getConnection() {
Connection conn = null;
try {
conn = connectionQueue.poll(15, TimeUnit.MINUTES);
}
catch(InterruptedException e) {
e.printStackTrace();
}
catch(SQLException e) {
e.printStackTrace();
}
return conn;
}
// Returns a connection to the connection queue.
public void returnConnectionToManager(Connection conn) {
connectionQueue.add(conn);
}
// Called on the last line of the application program's dispatcher.
// Closes all active connections (which will only exist if there
// was a failure within one of the worker threads).
public void closeAllConnections() {
for(Connection conn : connectionQueue) {
try {
conn.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
}
}
Invocation example:
...
private ConnectionManager cm;
...
public Table(Map<String, Object> params, String method) {
...
cm = (ConnectionManager) params.get("cm");
}
// Execute a chunk of SQL code without requiring processing of a
// result set. Acquires connection from pool via cm.getConnection
// and releases connection via cm.returnConnectionToManager.
// (Database is just a helper class with simple methods for
// closing prepared statement, result sets, etc.)
private void execute(String sql) {
PreparedStatement ps = null;
Connection conn = null;
try {
conn = cm.getConnection();
ps = conn.prepareStatement(sql);
ps.execute();
}
catch (SQLException e) {
e.printStackTrace();
}
finally {
database.closePreparedStatement(ps);
cm.returnConnectionToManager(conn);
}
}
Your code looks good, but there is one serious problem, that clients of your API needs to take care of getting and releasing connection, one of them forget, and memory/resource leak is ready.
Make a one place in which you posts your queries to execute, in this place take connection, execute query and return the connection to the pool. It will secure you that the connections are returned. If you need to invoke multiple queries one after another in a single connection make the method accept an array or list of SQL queries to execute in order. The idea is to encapsulate each request to the db, so you manage all connections. It could be donethat you write an interface that has en execute(Connection conn) which you need to implement, and you could have then some Service that takes such object gives it a connection and then releases the resources back to connection pool.
Something like:
interface SqlWork {
execute(Connection conn);
}
SqlWork myWork = new SqlWork () {
execute(Connection conn) {
// do you work with the conn here
}
}
class SqlExecutionService {
ConnectionManager cm = ...;
public void execute(SqlWork sqlWork) {
Connection conn = null;
try {
conn = cm.getConnection();
sqlWork.execute(conn);
} catch (Your exceptions here) {
//serve or rethrow them
}
finally
{
if (conn!=null) {
cm.returnConnectionToManager(conn);
}
}
}
}
Example of use:
SqlExecutionService sqlExecService = ...;
sqlExecService.execute(myWork);

Trouble understanding Neo4j JDBC Transaction

I am pretty sure that this is a silly question, but I am totally confused here. I am used to code with neo4j-embedded-api and very new to neo4j-jdbc. I want to use Neo4j-JDBC. My Neo4J 2.1.7 instance is hosted on another computer which is accessible via 192.168.1.16:7474
I can create simple programs and write cypher queries and execute them. However, I would like to wrap all of them in a transactional block. Something like this:
try(Transaction tx = this.graphDatabaseService.beginTx())
{
ResultSet resultSet = connect.createStatement().executeQuery(cypherQuery);
tx.success();
}
My issue is that I do not know how to get the object of GraphDatabaseService from this:
Neo4jConnection connect = new Driver().connect("jdbc:neo4j://192.168.1.16:7474", new Properties());
Once I have the GraphDatabaseObject, I am assuming that I can use the transactional block like neo4j-embedded-api.
[My Objective]
What I am trying to attempt here is to have multiple queries to be sent over the network in a nested-transaction block and if any one of them fails, to rollback all of them.
My failed attempts till now:
I tried to read the neo4j-jdbc project hosted on github and from their test case (link here) I am assuming that to put a code in transaction block you have to connect.setAutoCommit(false); and then use commit() and rollback() functions respectively.
If you don't know JDBC then please read up on it, it's a remote API which works by executing statement (Cypher in this case) against a remote API.
There is no GraphDatabaseService object. Only statements and parameters.
For tx handling you either have one tx per statement or if you set connection.setAutoCommit(false); then the transaction runs until you call connection.commit()
Just to add to Michael Hunger's answer, I made this program which does multiple transactions and rolls back them. This is a very easy program. Hope it helps other noobies like me.
package rash.experiments.neo4j.jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.neo4j.jdbc.Driver;
import org.neo4j.jdbc.Neo4jConnection;
public class JdbcTest
{
public Neo4jConnection connect;
public JdbcTest() throws SQLException
{
this.connect = new Driver().connect("jdbc:neo4j://192.168.1.16:7474", new Properties());
}
public static void main(String args[])
{
JdbcTest test = null;
try
{
test = new JdbcTest();
test.connect.setAutoCommit(false);
long user1 = test.createUser("rash");
long user2 = test.createUser("honey");
test.createRelationship(user1, user2);
test.connect.commit();
}
catch(SQLException sqlException)
{
try
{
test.connect.rollback();
}
catch (SQLException e){}
}
}
private long createUser(String userId) throws SQLException
{
ResultSet resultSet = this.connect.createStatement().executeQuery("create (user:User {userId: '" + userId + "'}) return id(user) as id");
resultSet.next();
return resultSet.getLong("id");
}
private void createRelationship(long node1, long node2) throws SQLException
{
this.connect.createStatement().executeQuery("start user1 = node(" + node1 + "), user2 = node(" + node2 + ") create (user1) -[:KNOWS]-> (user2)");
throw new SQLException();
}
}
This program will never commit because method createRelationship() throws an error and the program never gets a chance to commit. Instead it rolls back.

How can I connect to a MySQL web server with Java?

Alright, so here's a little bit of background:
I am currently trying to develop a referral application. There is a link on our website where a user can refer their friends to our game server (Minecraft). It will input the referrer's information into a database (hosted on my website) and send a link to the "friend". The friend clicks on the link and enters their information (which also gets stored in the database). All of this is working great! (Yay!) So, now for the Java Plugin!
What is supposed to happen...
I have an Event Listener that will fire whenever a user logs into the game. Essentially, it would check the data base for the user's info, and if the user meets the criteria, then it will award them with their extra referral goodies.
What I am trying to do right now...
Right now, I am essentially trying to just get it to connect and display the id of the row, and the ref_id (Referral ID) when the plugin is enabled. But, I'm getting the following error:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
So, without further ado, here is my singular Java document. Of course, that is not the real username and login information to my database. ;) But I'm hoping someone here can tell me what's wrong, because I'm so lost, right now.
package com.arithia.plugins;
import java.sql.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class ArithiaReferrals extends JavaPlugin implements Listener {
// JDBC driver name and database URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://66.147.244.122:3306/graphja6_referrals";
// Database credentials
static final String USER = "fake_username";
static final String PASS = "fake_password";
#Override
public void onEnable() {
Connection conn = null;
Statement stmt = null;
try {
// STEP 2: Register JDBC driver
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// STEP 3: Open a connection
getLogger().info("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// STEP 4: Execute a query
getLogger().info("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "SELECT id, ref_id FROM referred_users";
ResultSet rs = stmt.executeQuery(sql);
// STEP 5: Extract data from result set
while(rs.next()) {
// Retrieve by column name
int id = rs.getInt("id");
int ref_id = rs.getInt("ref_id");
// Display values
getLogger().info("ID: " + id);
getLogger().info("Referral ID: " + ref_id);
}
// STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();
}catch(SQLException se) {
// Handle errors for JDBC
se.printStackTrace();
}finally{
// finally block used to close resources
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
} // nothing we can do
try {
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
} // End Finally try
} // end try
getLogger().info("Goodbye!");
getLogger().info("The [Arithia Referrals] plugin was enabled!");
}
#Override
public void onDisable() {
getLogger().info("The [Arithia Referrals] plugin was disabled!");
}
#EventHandler
public void onPlayerLogin(PlayerLoginEvent e) {
// "Check database for player..."
}
}
Other Information...
Database Name: graphja6_referrals
Database Table: referred_users
Note: I am not entirely sure that the DB_URL is correct... 66.147.244.122 is the correct IP, but I'm not entirely sure about the port or anything else, so if someone could verify that's correct, I'd be appreciative.
Thank you very much for your help.
Okay, so I'm just an idiot.
For anyone else getting this error, you need to whitelist the IP of the remote connection for this to work. It is a firewall thing, and depending on who you host with, there's probably a "Remote MySQL" option in the cPanel. Add the IP to the remote client that is trying to access the database, so it will be whitelisted.
Thank you to everyone who tried to help. <3

Executing JDBC MySQL query with this custom method

I've been doing my homework and I decided to re-write my vote4cash class which manages the mysql for my vote4cash reward system into a new class called MysqlManager. The MysqlManager class I've made needs to allow the Commands class to connect to mysql - done and it needs to allow the Commands class to execute a query - I need help with this part. I've had a lot more progress with the new class that I've made but I'm stuck on one of the last, most important parts of the class, again, allowing the commands class to execute a query.
In my MysqlManager class I have put the code to connects to MySql under
public synchronized static void createConnection() {
Now I just need to put the code that allows the Commands class to execute a query under this as well. I've researched and tried to do this for a while now, but I've had absolutely no luck.
The entire MysqlManager class:
package server.util;
/*
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
*/
import java.sql.*;
import java.net.*;
import server.model.players.Client;//Will be needed eventually so that I can reward players who have voted.
/**
* MySQL and Vote4Cash Manager
* #author Cloudnine
*
*/
public class MysqlManager {
/** MySQL Connection */
public static Connection conn = null;
public static Statement statement = null;
public static ResultSet results = null;
public static Statement stmt = null;
public static ResultSet auth = null;
public static ResultSet given = null;
/** MySQL Database Info */
public static String DB = "vote4gold";
public static String URL = "localhost";
public static String USER = "root";
public static String PASS = "";
public static String driver = "com.mysql.jdbc.Driver"; //Driver for JBDC(Java and MySQL connector)
/** Connects to MySQL Database*/
public synchronized static void createConnection() {
try {
Class.forName(driver);
conn = DriverManager.getConnection(URL + DB, USER, PASS);
conn.setAutoCommit(false);
stmt = conn.createStatement();
Misc.println("Connected to MySQL Database");
}
catch(Exception e) {
//e.printStackTrace();
}
}
public synchronized static void destroyConnection() {
try {
statement.close();
conn.close();
} catch (Exception e) {
//e.printStackTrace();
}
}
public synchronized static ResultSet query(String s) throws SQLException {
try {
if (s.toLowerCase().startsWith("select")) {
ResultSet rs = statement.executeQuery(s);
return rs;
} else {
statement.executeUpdate(s);
}
return null;
} catch (Exception e) {
destroyConnection();
createConnection();
//e.printStackTrace();
}
return null;
}
}
The snippet of my command:
if (playerCommand.equals("claimreward")) {
try {
PreparedStatement ps = DriverManager.getConnection().createStatement("SELECT * FROM votes WHERE ip = hello AND given = '1' LIMIT 1");
//ps.setString(1, c.playerName);
ResultSet results = ps.executeQuery();
if(results.next()) {
c.sendMessage("You have already been given your voting reward.");
} else {
ps.close();
ps = DriverManager.getConnection().createStatement("SELECT * FROM votes WHERE ip = hello AND given = '0' LIMIT 1");
//ps.setString(1, playerCommand.substring(5));
results = ps.executeQuery();
if(results.next()) {
ps.close();
ps = DriverManager.getConnection().createStatement("UPDATE votes SET given = '1' WHERE ip = hello");
//ps.setString(1, playerCommand.substring(5));
ps.executeUpdate();
c.getItems().addItem(995, 5000000);
c.sendMessage("Thank you for voting! You've recieved 5m gold!");
} else {
c.sendMessage("You haven't voted yet. Vote for 5m gold!");
}
}
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return;
How the command works:
When a player types ::commandname(in this case, claimreward), the commands function will be executed. This isn't the entire commands class, just the part that I feel is needed to be posted for my question to be detailed enough for a helpful answer.
Note: I have all my imports.
Note: Mysql connects successfully.
Note: I need to make the above command code snippet able to execute mysql queries.
Note: I prefer the query to be executed straight from the command, instead of from the MysqlManager, but I will do whatever I need to resolve this problem.
I feel that I've described my problem detailed and relevantly enough, but if you need additional information or understanding on anything, tell me and I'll try to be more specific.
Thank you for taking the time to examine my problem. Thanks in advance if you are able to help.
-Alex
Your approach is misguided on many different levels, I can't even start to realize what should be done how here.
1) Don't ever use static class variables unless you know what you do there (and I'm certain, you don't)
2) I assume there is a reason you create your own jdbc connection (e.G. its part of your homework) if not, you shouldn't do that. I see you use DriverManager and PreparedStatement in one part, you should continue to use them.
3) Your approach seems to intend to start with a relative good code base (your command part) and then goes to a very low-level crude approach on database connections (your MysqlManager) unless really necessary and you know what you do, you should stay on the same level of abstraction and aim for the most abstract that fits your needs. (In this case, write MysqlManager the way you wrote Command)
4) In your previous question (that you just assumed everybody here has read, which is not the case) you got the suggestion to redesign your ideas, you should do that. Really, take a class in coding principles learn about anti-patterns and then start from scratch.
So in conclusion: Write at least the MysqlManager again, its fatally broken beyond repair. I'm sorry. Write me an email if you have further questions, I will take my time to see how I can help you. (an#steamnet.de)

Categories

Resources