hibernate interceptors : afterTransactionCompletion - java

I wrote a Hibernate interceptor :
public class MyInterceptor extends EmptyInterceptor {
private boolean isCanal=false;
public boolean onSave(Object entity, Serializable arg1, Object[] arg2, String[] arg3, Type[] arg4) throws CallbackException {
for(int i=0;i<100;i++){
System.out.println("Inside MyInterceptor(onSave) : "+entity.toString());
}
if(entity instanceof Canal){
isCanal=true;
}
return false;
}
public void afterTransactionCompletion(Transaction tx){
if(tx.wasCommitted()&&(isCanal)){
for(int i=0;i<100;i++){
System.out.println("Inside MyInterceptor(afterTransactionCompletion) : Canal was saved to DB.");
}
}
}
I can see the method onSave executing fine, but afterTransactionCompletion method never gets executed even though the transaction is successfully commited to the database.
I need a way to track every time a Canal object is successfully saved to the DB and react by printing some messages. is that feasible ? and how ?
Here is the method I use to save objects in the DB :
public static Object enregObjet(Object obj) throws UpdateException,
EnregException, ErreurException {
Transaction tx = null;
try {
Session s = InterfaceBD.currentSession();
tx = s.beginTransaction();
try {
// Positionner les champs dteUti et dteUtiModif
Method dteUtiSetter = null;
;
// Objet en insertion
dteUtiSetter = obj.getClass().getMethod("setDteUti",
new Class[] { java.util.Date.class });
dteUtiSetter.invoke(obj, new Object[] { new java.util.Date() });
} catch (NoSuchMethodException ex) {
;// Le champ dteUtiModif n'existe pas
}
// Enregistrer
IardNum.numeroterCode(obj);
IardNum.numeroterId(obj);
s.save(obj);
s.flush();
tx.commit();
try {
String id = "";
// Positionner les champs dteUti et dteUtiModif
Method idGetter = null;
// Objet en insertion
idGetter = obj.getClass().getMethod("getId");
id = (String) idGetter.invoke(obj);
Connection conn = InterfaceBD.getConn();
IardGenerator3.cleanSeq(id, conn);
conn.close();
} catch (NoSuchMethodException ex) {
;// Le champ dteUtiModif n'existe pas
}
catch(ClassCastException ex){
;//just ignore it because we are dealing with a PK class (e.g : CausesAnnexesSinistrePK).
}
s.clear();
return obj;
}

I would suggest using DAO design template. Basicaly you just create an class, which will save Canal object and use it everywhere where you need to save it.
In this class you can add all logic you need.

Related

oracle tomcat-jdbc pool connection always use same session

I have a spring boot tomcat-jdbc configuration with:
DataSource ds = new DataSource();
ds.setDriverClassName("oracle.jdbc.OracleDriver");
ds.setUrl("jdbc:oracle:thin:#10.101.7.16:1521:SIAMA");
ds.setUsername("xxx");
ds.setPassword("ccc");
ds.setInitialSize(10);
ds.setMaxActive(15);
ds.setMaxIdle(10);
ds.setMinIdle(5);
When i start tomcat i see in my database session 10 initial inactive session.
The problem is when the users starts app always user the same connection in database. The other 9 never use.
This is my code:
package com.csi_ti.itaca.custom.general.server.service;
import org.apache.tomcat.jdbc.pool.DataSource;
public class GeneralBusinessServiceImpl implements GeneralBusinessService {
public Connection conn;
ConversionUtil convert = new ConversionUtil();
#Autowired
#Qualifier("plsqlDataSource")
private DataSource plsqlDataSource;
#PostConstruct
public void init() throws SQLException {
System.out.println(">>>>> GeneralBusinessService Con 1");
conn = plsqlDataSource.getConnection();
}
public Connection obtenerConexion() {
System.out.println("Obtener conexion......................");
try {
if ( conn.isClosed()) {
System.out.println(">>>>>>>>>>> Conexión cerrada");
return conn = plsqlDataSource.getConnection();
}
else {
System.out.println(">>>>>>>>>>> Conexión ABIERTA");
conn.close();
conn = null;
System.out.println(">>>>>>>>>>> La cerramos");
conn = plsqlDataSource.getConnection();
System.out.println(">>>>>>>>>>> La volvemos a abrir " + conn.toString());
return conn;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public void ejecutarConsulta() {
//System.out.println("Entramos a ejecutar consulta <<<<<<<<<<<<<<<<<<<*<*<*<*<*<*<*<*<*<*>*>*<*<" );
//System.out.println("Antes de Pob ****: " + conn);
System.out.println(">>>>> GeneralBusinessService Con 2");
PAC_SHWEB_PROVEEDORES llamada = new PAC_SHWEB_PROVEEDORES(conn);
try {
llamada.ejecutaPAC_SHWEB_PROVEEDORES__F_LISTA_TELEFONOS_EXPEDIENTE(new BigDecimal("906000060"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//System.out.println("Pob ****: " + llamada.toString());
}
private Generico0DTO getDTO(Object obj){
System.out.println(">>>>> GeneralBusinessService Con 3");
Generico0DTO dto = new Generico0DTO();
List<Generico0.MapObject> listMapObjects= new ArrayList<Generico0.MapObject>();
if (obj!=null){
if (obj instanceof List<?>){
for (Map m :(List<Map>)obj){
Generico0.MapObject mapObject = new Generico0.MapObject();
mapObject.setMap(m);
listMapObjects.add(mapObject);
}
}else if (obj instanceof Map){
Generico0.MapObject mapObject = new Generico0.MapObject();
mapObject.setMap((Map)obj);
listMapObjects.add(mapObject);
}else if (obj instanceof BigDecimal){
Generico0.MapObject mapObject = new Generico0.MapObject();
Map map = new HashMap<String,BigDecimal>();
map.put("RETURN",obj);
mapObject.setMap(map);
listMapObjects.add(mapObject);
}
}
dto.setMapObject(listMapObjects);
return dto;
}
#Override
public Generico0 ejecutaPAC(String pac, String function, boolean tratarMensajes, Object... parameters) {
System.out.println(">>>>> GeneralBusinessService Con 4");
Map map;
try {
Class<?> clazz = Class.forName("com.csi_ti.itaca.custom.general.server.jdbc." + pac);
Constructor<?> constructor = clazz.getConstructor(Connection.class);
Object pacInstance = constructor.newInstance(conn);
String methodName = "ejecuta"+pac+"__"+function;
////System.out.println("LLamada pac: "+methodName);
Class<?>[] parameterTypes = new Class<?>[parameters.length];
Object[] parameterInput = new Object [parameters.length];
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].getClass().equals(Integer.class)) {
parameterTypes[i] = BigDecimal.class;
parameterInput[i] = new BigDecimal((Integer) parameters[i]);
} else {
parameterTypes[i] = parameters[i].getClass();
parameterInput[i] = parameters[i];
}
}
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
map = (Map) method.invoke(pacInstance, parameterInput);
if(tratarMensajes){
Object obj = Util.tratarRETURNyMENSAJES(map);
return getDTO(obj);
} else {
return getDTO(map);
}
} catch (Exception e) {
//System.out.println("Error_Service:"+e);
return null;
}
}
}
I need the pool to balance sessions for each request.
Any help ?
Thank you.
Your #PostConstruct method is bad and wrong. Instead of having the Connection conn variable that you initialize in your post construct, get a connection from the datasource whenever you need to use one.
try(Connection con = plsqlDataSource.getConnection()) {
// Do something with con
}
It's a lot simpler than what you've written too!
Your obtenerConexion() is unnecessary too. You are using a connection pool, you're not writing one.
You also shouldn't use org.apache.tomcat.jdbc.pool.DataSource directly, but rather javax.sql.DataSource.

JPA trying to remove entity

I'm using a method to save a object in database, see:
#Transactional(propagation = Propagation.REQUIRED)
public void movimentarCaixa(String descricao,
AbstractBean donoCaixa, FormaPagamento formaPagamento, HistoricoPagamento historicoPagamento, double valor) {
try {
HistoricoCaixa historicoCaixa;
if (donoCaixa instanceof Dentista) {
logger.info("Iniciando movimentacao caixa para Dentista. Valor: "+valor);
historicoCaixa = new HistoricoCaixaDentista();
((Dentista) donoCaixa).setCaixaSaldoAtual(((Dentista) donoCaixa).getCaixaSaldoAtual()
+ valor);
((HistoricoCaixaDentista) historicoCaixa).setDentista((Dentista) donoCaixa);
} else if (donoCaixa instanceof Paciente) {
logger.info("Iniciando movimentacao caixa para Paciente. Valor: "+valor);
historicoCaixa = new HistoricoCaixaPaciente();
((Paciente) donoCaixa).setCaixaSaldoAtual(((Paciente) donoCaixa).getCaixaSaldoAtual()
+ valor);
((HistoricoCaixaPaciente) historicoCaixa).setPaciente((Paciente) donoCaixa);
} else if (donoCaixa instanceof Clinica) {
logger.info("Iniciando movimentacao caixa para Clinica. Valor: "+valor);
historicoCaixa = new HistoricoCaixaClinica();
Clinica clinica = (Clinica) donoCaixa;
if (formaPagamento == null || formaPagamento.getCodigo().equals("DINHEIRO")){
clinica.setSaldoDinheiro(clinica.getSaldoDinheiro() + valor);
} else if (formaPagamento.getCodigo().equals("CHEQUE")) {
clinica.setSaldoCheque(clinica.getSaldoCheque() + valor);
} else if (formaPagamento.getCodigo().equals("DEBITO")) {
clinica.setSaldoDebito(clinica.getSaldoDebito() + valor);
} else if (formaPagamento.getCodigo().equals("CREDITO")) {
clinica.setSaldoCredito(clinica.getSaldoCredito() + valor);
}
((HistoricoCaixaClinica) historicoCaixa).setClinica((Clinica) donoCaixa);
} else {
throw new MyException(
"O parametro 'donoCaixa' deve ser do tipo Dentista, Paciente ou Clinica");
}
historicoCaixa.setDataHora(historicoPagamento.getDataHora());
historicoCaixa.setDescricao(descricao);
historicoCaixa.setFormaPagamento(formaPagamento);
historicoCaixa.setHistoricoPagamento(historicoPagamento);
historicoCaixa.setUsuario((Usuario) SessionContext.getInstance().getUsuarioLogado());
historicoCaixa.setValor(valor);
this.getDao().save(historicoCaixa);
} catch (Exception e) {
e.printStackTrace();
}
}
I call this method twice, in the first time the variable "historicoCaixa" receives "new HistoricoCaixaPaciente()" instance and in second time this same variable receives "new HistoricoCaixaClinica()". The problem is that in second call i got error:
Caused by: java.lang.IllegalArgumentException: Removing a detached instance br.com.odontonew.financeiro.bean.HistoricoCaixaClinica#94
I imagine that JPA is trying to remove HistoricoCaixaClinica#94 but i don't want this, how can i avoid it ?

JDBC rollback method not behaving like expected

I'm developing a software in Java processing data in a MySQL database and I'm facing an issue due to a rollback call not doing what I was expecting ... My function is composed of two queries and I need to rollback if the second query is not executed (or with errors) in order to keep consistent data. Here is my code, I've been doing a syntax error on purpose for throwing an error during the second query execution. The rollback method is called but my first statement is executed and committed in my database, can you explain me why ?
#Override
public void updateIndicatorRemainingTimeAndExecuted(int id) throws ServiceException {
PreparedStatement stmt = null;
Connection con = null;
String query1 = "UPDATE "+indicatorSchedulerTable+" i "
+"JOIN adv_frequency f ON i.id_frequency = f.id_frequency "
+"SET i.ind_remainingtime = i.ind_remainingtime + f.frq_seconds WHERE id_indicator = ?";
String query2 = "UPDATE "+indicatorSchedulerTable
+" SET ind_executing = WHERE id_indicator = ?";
try {
con = mySQLManipulator.getConnection();
stmt = con.prepareStatement(query1);
stmt.setInt(1,id);
/*Updating remaining time*/
stmt.executeUpdate();
stmt.close();
/*Updating executing status*/
stmt = con.prepareStatement(query2);
stmt.setInt(1,id);
stmt.executeUpdate();
con.commit();
} catch (SQLException e) {
try {
con.rollback();
System.out.println("ROLLBACK OK");
} catch (SQLException e1) {
throw new ServiceException("Problème de rollback lors de la mise à jour dans la fonction \"updateIndicatorRemainingTimeAndExecuted\" : "+e1.getMessage()+e.getMessage());
}
throw new ServiceException("Problème lors de la mise à jour dans la fonction \"updateIndicatorRemainingTimeAndExecuted\" : "+e.getMessage());
} finally {
handleDatabaseClosure(con, stmt, null);
}
}
I'm also using a pool of connection like this maybe the error is because of this :
public class JDBCManipulator {
private static final BasicDataSource dataSource = new BasicDataSource();
private DBType type = null;
private String URI = null;
public JDBCManipulator(DBType type, String URI, String user, String password) throws Exception {
if(type == DBType.PHOENIX){
Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
} else if(type == DBType.MYSQL) {
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl(URI);
dataSource.setUsername(user);
dataSource.setPassword(password);
dataSource.setMaxActive(20);
dataSource.setDefaultAutoCommit(false);
} else {
throw new Exception("Le type fournit ("+type+") pour l'initialisation de JDBCManipulator n'est pas connu ...");
}
this.type = type;
this.URI = URI;
}
public Connection getConnection() throws SQLException {
Connection conn = null;
if(type == DBType.MYSQL){
conn = dataSource.getConnection();
} else {
conn = DriverManager.getConnection(URI);
}
return conn;
}
}
You have to set autoCommit of the connection to false.
con.setAutoCommit(false);
Otherwise, each executed update would be commited immediatelly.
In addition, use a storage engine that supports transactions like InnoDB. MyISAM does not support transactions.
I was able to get everything working by replacing ENGINE=MyISAM with ENGINE=INNODB in my CREATE statement. Thank you all for your help!

Hibernate PersistenceContext session Flush

i want to know when does hibernate fulshes the context session when i call session= session.getCurrentSession()
The thing is i have 2 methods in my dao calling getCurrentSession(), when i process the update making the call to getCurrentSession() the entitys are empty:
SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];...)
How can i make this entitys persist from the select method to the update method?
Here are my methods:
public List<SystemConfiguration> getListConfigurations() {
List<SystemConfiguration> lista = new ArrayList<SystemConfiguration>();
Session session = null;
Query query = null;
String sql = "from SystemConfiguration where description = :desc";
try {
/* BEFORE
session = SessionFactoryUtil.getInstance().getCurrentSession();
#SuppressWarnings("unused")
Transaction ta = session.beginTransaction(); */
//FOLLOWING LINE SOLVED THE PROBLEM
session = SessionFactoryUtil.getInstance().openSession();
query = session.createQuery(sql);
query.setString("desc", "configuracion");
lista = query.list();
return lista;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void updateConfigurations(List<SystemConfiguration> configs) throws Exception{
Session sess = null;
Transaction tx = null;
try {
//BEFORE
//sess = SessionFactoryUtil.getInstance().getCurrentSession();
//FOLLOWING LINE SOLVED THE PROBLEM
sess = SessionFactoryUtil.getInstance().openSession(new SystemConfigurationInterceptor());
tx = sess.beginTransaction();
for (SystemConfiguration sys : configs) {
sess.update(sys);
}
tx.commit();
} // try
catch (Exception e) {
e.printStackTrace();
if (tx != null && tx.isActive()) {
tx.rollback();
} // if
throw e;
}
}
And this is my interceptor:
public class SystemConfigurationInterceptor extends EmptyInterceptor {
private int updates;
private int creates;
private int loads;
public void onDelete(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
// do nothing
}
// This method is called when Entity object gets updated.
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if ( entity instanceof SystemConfiguration ) {
updates++;
for ( int i=0; i < propertyNames.length; i++ ) {
if ( "updated_at".equals( propertyNames[i] ) ) {
currentState[i] = new Timestamp(Calendar.getInstance().getTime().getTime());
return true;
}
}
}
return false;
}
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof SystemConfiguration ) {
loads++;
}
return false;
}
// This method is called when Entity object gets created.
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof SystemConfiguration ) {
creates++;
for ( int i=0; i<propertyNames.length; i++ ) {
if ( "updated_at".equals( propertyNames[i] ) ) {
state[i] = new Timestamp(Calendar.getInstance().getTime().getTime());
return true;
}
}
}
return false;
}
public void afterTransactionCompletion(Transaction tx) {
if ( tx.wasCommitted() ) {
System.out.println("Creations: " + creates + ", Updates: " + updates +", Loads: " + loads);
}
updates=0;
creates=0;
loads=0;
}
Hibernate will flush when you tell it to and when the current transaction is "closed" (usually when the DB connection is returned to the pool somehow).
So the answer to your question depends on which framework you use. With Spring, the session is flushed when the outermost #Transactional method returns.
Your "solution" above will not work for long since it never closes the session. While it returns a result, it will leak a database connection so after a few calls, you will run out of connections.
Also your question doesn't really make sense. SELECT doesn't change objects, so they don't need to be "persisted" before you change them.
After changing them in updateConfigurations(), Hibernate can chose not to write them into the database immediately and just update the cache.
Eventually, if you configured everything correctly, Spring will commit the transaction and that will flush the cache. But when you use Spring, you should never create open and close sessions because it will mess with what Spring is doing.

KryoNet - How do I start client with Boolean xx = new clientprocess(); to get a return value?

I'm creating a little JAVA multiplayer game using kryonet to manage the Server/Client part. I have a little question: in my server program, I start a client with a new called method, "on" a Boolean:
Boolean xx = new clientprocess();
In my clientprocess();, I get the new client();, client.connect and the class registration for the new client. It also contain the 3 methods conn/receive/disconn. Now, I don't know how to use the return data to get a boolean value. (In fact, I just use this client to connect to a BDD server to get pass/user checking so i just need a Boolean back to treat with client.)
I have tried stopping and closing the client connection, remove listener, and so forth, but I can't get out of the Received(); or Disconnected(); method. And because they are Void method, I cannot make the return value inside the methods. (My actual return is at the end of clientprocess(); after disconnected(); but the code is never reached.)
Is there anything to completely stop the client process to reach my return boolean; statement and continue my login process?
Here is a part of my code, started with Boolean xx = new loginProcess():
boolean loginProcess(final String usernamereceived, final String passwordreceived, final int todologin){
System.out.println("In LOGIN PROCESS");
final Client loginclient = new Client();
Kryo kryologin = loginclient.getKryo(); //encodage des packets
//kryo.register(byte[].class); Si on utilise le system de BYTE
kryologin.register(Packet0LoginRequest.class);
kryologin.register(Packet1LoginAnswer.class);
kryologin.register(Packet2Message.class);
kryologin.register(Packet2login.class);
kryologin.register(Packet2login1.class);
kryologin.register(Packet2login2.class);
kryologin.register(InetSocketAddress.class); //useless
kryologin.register(Inet4Address.class); //useless
kryologin.register(PacketupdateTest.class);
kryologin.register(LoginServerConnect.class);
kryologin.register(LoginServerConnectAnswer.class);
kryologin.register(java.util.ArrayList.class);
kryologin.register(java.util.List.class);
kryologin.register(User.class);
kryologin.register(String[].class);
System.out.println("Login registration over");
loginclient.start();
loginclient.addListener(new Listener() {
#Override
public void connected (Connection connection1) {
System.out.println("Connected login");
LoginServerConnect loginserverconnect = new LoginServerConnect();
connection1.sendTCP(loginserverconnect);
System.out.println("Loginserverconnect sended: "+loginserverconnect);
}
#Override
public void received (Connection connection1, Object object) {
System.out.println("Reception dun packet du login server HASH: "+object);
if (object instanceof LoginServerConnectAnswer) {
if (todologin==1){
System.out.println("LOGIN PROCESS");
Packet2login1 loginask = new Packet2login1();
loginask.Password = passwordreceived;
loginask.Username = usernamereceived;
connection1.sendTCP(loginask);
System.out.println("Fichier packet2login1 sended: User *"+loginask.Username+"*, et pass: *"+loginask.Password);
}
else if (todologin == 2){
System.out.println("ADD USER PROCESS");
Packet2AddUser adduserask = new Packet2AddUser();
adduserask.password = passwordreceived;
adduserask.username = usernamereceived;
connection1.sendTCP(adduserask);
System.out.println("Fichier packet2AddUser sended: user *"+adduserask.username+"*, et pass: *"+adduserask.password);
}
}
if (object instanceof Packet2login2){
Boolean result = ((Packet2login2) object).Result;
String usernamefromlogin = ((Packet2login2) object).Username;
System.out.println("reception de la reponse de login: **"+result+"** pour l'utilisateur: "+usernamefromlogin);
//creation nouvel user - ajout if useramefromlogin == usernamereceived
user.name = usernamefromlogin;
if(result){
System.out.println("user received from login server: "+ user.name+", and KryoId: "+Kryoid); System.out.println("Kryoid du client actu (depuis le packet0 C-S: "+ Kryoid);
int lastid = players.lastIndexOf(user);
if (players.lastIndexOf(user) == 0){
user.Id = lastid;
}
else {user.Id = lastid +1;}
players.add(user);
System.out.println("Players.add over");
System.out.println("BDD result returned0: "+mtx);
mtx = true;
System.out.println("BDD result returned1: "+mtx);
/* connection1.close();
loginclient.close();
loginclient.stop();*/
loginclient.removeListener(this);
}
else{
System.out.println("Username or Password incorrect for user: "+ usernamefromlogin);
mtx = false;
}
}
if (object instanceof Packet2AddUser1){
Boolean addresult = ((Packet2AddUser1) object).adduserresult;
String usernamefromlogin = ((Packet2AddUser1) object).username;
if(addresult){
System.out.println("Nouvel utilisateur enregistre en BDD: *"+ usernamefromlogin);
mtx = true;
}
else {
System.out.println("Erreur lors de lenregistrement en BDD pour: *"+ usernamefromlogin);
mtx = false;
}
}
System.out.println("END of received");
}
#Override
public void disconnected (Connection connection1){
System.out.println("public void DISCONN passé OK cote LISTENER");
}
});
try {
System.out.println("loginclient.connect");
loginclient.connect(loginstimeout, loginsip, loginsPORT);
System.out.println("end of conn");
} catch (IOException ex) {
Logger.getLogger(NetworkListener.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("BDD result returned: "+mtx);
return mtx;
}
Use Kryonet RMI Remote Method Invocation
or make your method return void and add a boolean member to your main class and when you receive LoginServerConnectAnswer, just change boolean member to true and call a method in your main class to process login

Categories