MYSQL
HOME > DB > MYSQL
2018.09.30 / 22:32

Execute Java code from MySQL Trigger

GAScripter
Ãßõ ¼ö 163

The other day I was looking to trigger some Java code from a MySQL Trigger. The reason for this was I am writing a service that should monitor events from multiple other server components that all already have a database connection. So instead of opening yet another socket for this service, why not use the existing database engine? Our events are stored in the database anyway and the server cannot operate when the database is down (so I would not be introducing any extra dependencies). Reading around on the internet however had me believe that executing external code from a MySQL trigger isn't possible. 

Yet the more comments I read from people saying this was not possible the more I was refusing to believe just that :-) Instead it got me thinking.. maybe there was some SQL statement that allows for some kind of interoperability? Quickly I was focusing my thoughts around the SELECT ... INTO OUTFILE statement. My idea was to either load some dummy data into a temporary file and use OS file/dir monitoring events to be informed about this, or writing to a named pipe so to trigger the other end of the pipe (which would be my Java code). The idea of having physical disk access every time a trigger gets fired didn't strike me as very good so I decided to go for the named pipes.

I started out coding for Windows because that's the primary target platform for the service I am working on. I wrote a JNI library to create a named pipe and wait for any action on that pipe asynchronously. Once any action happens the library will attach back to the JVM - thanks to Adam by the way for his info on asynchronous callbacks using JNI - and call the previously registered Java class/method. Rather against my expectations this immediately gave the desired result when manually firing the SELECT ... INTO OUTFILE statement passing the named pipe as outfile. The JNI end of the named pipe got triggered! As an unfortunate side effect however I also received an exception in MySQL. It seems SELECT ... INTO OUTFILE can only write to new files, not to existing files (a named pipe once created behaves as an existing file). At first I was very disappointed about this but after some further contemplation I realized that the exception doesn't matter in this case since I only required the pipe to be signalled, not any actual data to be written. And when adding the statement to an actual trigger I could declare an empty continue handler inside it to suppress the sql exception.

Now that all pieces were falling into place I created a nice Java library with an interface for registering a trigger with MySQL and receiving the trigger action in Java. The JAR includes the required DLL to create the named pipe and wait for connection on the pipe asynchronously. The DLL is included for both 32 bits and 64 bits Windows and the code will check runtime which version is required (using the "sun.arch.data.model" system property). For now the package is Windows-only but when I have some time I will make this Linux compatible as well, don't worry. update 12-06-2012: the package will now also work on Linux; note however that on Linux it works slightly different because named pipes didn't have the same effect; so instead it mounts a memory file system on a temporary directory to function as named pipe substitue; and then listens using inotify

I have not tested performance extensively but since the solution uses in-memory communication and a blocking read in a separate thread until trigger data comes in performance must be decent at least. Usage so far in my own project has comfirmed this. If someone has actual numbers please leave a comment.

Now let's get down to the code. Here is the interface you need to implement:

import nl.rocksolit.db.*;

public interface DatabaseTriggerMonitor
{
public void onTrigger(DatabaseTrigger trigger);
}


And here is how to activate a trigger:

import nl.rocksolit.db.*;

DatabaseTrigger dbTrigger = new DatabaseTrigger(conn, "EventLog", DatabaseTrigger.Time.AFTER, DatabaseTrigger.Event.INSERT, this);

..
..


and to release it when you're done:

..
..

dbTrigger.drop();


Please note that the trigger is given an automatically generated name in the form on<TableName><Time><Event> so the above trigger would be named onEventLogAfterInsert in MySQL. Any existing trigger with this name will be dropped! This is done because MySQL can only store one trigger per action time and event. Calling .drop() to release the trigger is optional but wise since it will remove the trigger in MySQL and nicely close the named pipe. If not dropped before your Java code terminates every trigger in MySQL will still result in firing the action and thus trying to write to the pipe. While this won't damage anything it is undoubtedly going to cost you performance.

limitations and known issues:
- tested on Windows 7 (x64) with MySQL 5.1.49 and CentOS 5.4 (x64) with MySQL 5.0.77
- up to 64 triggers can be registered simultaneously (ie. created without calling drop on one or more first)
- only one trigger can exist for the same table-time-event combination at the same time, creating a subsequent one will drop the previous one
- only tested with Java code running on same machine as MySQL, however under Windows it should also work if both ends are running in the same network
- the database user in effect must have the MySQL FILE privilege

This package is © 2012 by Arthur de Vaan/RocksolIT. It may be used and distributed for free as long as the included license is obeyed.
If the package saves you time and/or headaches, or if it helps you make some bucks please consider a small donation and/or note of appreciation.

jdbtrigger.jar



MySQL Æ®¸®°Å¿¡¼­ Java ÄÚµå ½ÇÇà°¡´É! ¶Ç ´Ù¸¥ RocksolIT ¢â ¼Ö·ç¼Ç


¿äÁò¿¡´Â MySQL Æ®¸®°Å¿¡¼­ ÀϺΠJava Äڵ带 Æ®¸®°ÅÇÏ·Á°íÇÕ´Ï´Ù. ±× ÀÌÀ¯´Â ÀÌ¹Ì µ¥ÀÌÅͺ£À̽º ¿¬°áÀÌÀÖ´Â ´Ù¸¥ ¿©·¯ ¼­¹ö ±¸¼º ¿ä¼ÒÀÇ À̺¥Æ®¸¦ ¸ð´ÏÅÍÇؾßÇÏ´Â ¼­ºñ½º¸¦ ÀÛ¼ºÇϱâ À§Çؼ­ÀÔ´Ï´Ù. µû¶ó¼­ÀÌ ¼­ºñ½º¸¦À§ÇÑ ¶Ç ´Ù¸¥ ¼ÒÄÏÀ» ¿©´Â ´ë½Å ±âÁ¸ µ¥ÀÌÅͺ£À̽º ¿£ÁøÀ» »ç¿ëÇÏÁö ¾Ê´Â ÀÌÀ¯´Â ¹«¾ùÀԴϱî? ¾î·µç ¿ì¸®ÀÇ À̺¥Æ®´Â µ¥ÀÌÅͺ£À̽º¿¡ ÀúÀåµÇ¸ç µ¥ÀÌÅͺ£À̽º°¡ ´Ù¿îµÇ¾úÀ» ¶§ ¼­¹ö°¡ ÀÛµ¿ÇÏÁö ¾Ê½À´Ï´Ù (µû¶ó¼­ Ãß°¡ Á¾¼Ó¼ºÀ» µµÀÔÇÏÁö ¾ÊÀ» °ÍÀÔ´Ï´Ù). ±×·¯³ª ÀÎÅͳݿ¡¼­ Àд °ÍÀº MySQL Æ®¸®°Å¿¡¼­ ¿ÜºÎ Äڵ带 ½ÇÇàÇÏ´Â °ÍÀÌ ºÒ°¡´ÉÇÏ´Ù°í »ý°¢Çß½À´Ï´Ù.

±×·¯³ª »ç¶÷µéÀÌ ÀÐÁö ¸øÇß´ø ÀÇ°ßÀ» ´õ ¸¹ÀÌ ÀÐÀ¸¸é ´õ ¸¹Àº °ÍÀ» ¹ÏÀ» ¼ö ¾ø°ÔµÇ¾ú½À´Ï´Ù .--) ´ë½Å Àú¿¡°Ô »ý°¢ÀÌ µé¾ú½À´Ï´Ù. ¾î¶² Á¾·ùÀÇ »óÈ£ ¿î¿ë¼ºÀ» Çã¿ëÇÏ´Â SQL ¹®ÀÌÀÖÀ» ¼öµµ ÀÖ½À´Ï´Ù. ³ª´Â SELECT ... INTO OUTFILE ¹®¿¡ ´ëÇØ ºü¸£°Ô »ý°¢À» ÁýÁßÇÏ°íÀÖ¾ú½À´Ï´Ù. ³» »ý°¢Àº Àӽà ÆÄÀÏ¿¡ ´õ¹Ì µ¥ÀÌÅ͸¦·ÎµåÇÏ°í OS ÆÄÀÏ / µð·ºÅ͸® ¸ð´ÏÅ͸µ À̺¥Æ®¸¦ »ç¿ëÇÏ¿©ÀÌ Á¤º¸¸¦ ¹Þ°Å³ª ¸í¸í µÈ ÆÄÀÌÇÁ¿¡ ¾²¸é ÆÄÀÌÇÁÀÇ ´Ù¸¥ ÂÊ ³¡ (³» Java ÄÚµå)ÀÌ Æ®¸®°ÅµË´Ï´Ù. . ¹æ¾Æ¼è°¡ ¹ß»ç µÉ ¶§¸¶´Ù ¹°¸®Àû ÀÎ µð½ºÅ© Á¢±ÙÀ»ÇÑ´Ù´Â ¾ÆÀ̵ð¾î´Â ³ª¿¡°Ô Å« Ÿ°ÝÀ»ÁÖÁö ¾Ê¾ÒÀ¸¹Ç·Î ¸í¸í µÈ ÆÄÀÌÇÁ·Î °¡±â·Î °áÁ¤Çß½À´Ï´Ù.

Windows¿¡¼­ ÄÚµù ÀÛ¾÷À» ½ÃÀÛÇÑ ÀÌÀ¯´Â ÀÌ°ÍÀÌ ³»°¡ ÀÛ¾÷ÁßÀÎ ¼­ºñ½ºÀÇ ÁÖ¿ä ´ë»ó Ç÷§ÆûÀ̱⠶§¹®ÀÔ´Ï´Ù. JNI ¶óÀ̺귯¸®¸¦ ÀÛ¼ºÇÏ¿© ¸í¸í µÈ ÆÄÀÌÇÁ¸¦ ÀÛ¼ºÇÏ°í ÇØ´ç ÆÄÀÌÇÁÀÇ ¸ðµç Á¶Ä¡¸¦ ºñµ¿±â ÀûÀ¸·Î ±â´Ù¸³´Ï´Ù. ¾×¼ÇÀÌ ¹ß»ýÇϸé JNI¸¦ »ç¿ëÇÏ¿© ºñµ¿±â Äݹ鿡 ´ëÇÑ Á¤º¸¸¦ ¾ò ½À´Ï´Ù. Adam ´öÅÿ¡ ¶óÀ̺귯¸®°¡ JVM¿¡ ´Ù½Ã ÷ºÎµË´Ï´Ù.- ÀÌÀü¿¡ µî·Ï µÈ Java Ŭ·¡½º / ¸Þ¼Òµå¸¦ È£ÃâÇϽʽÿÀ. ¿ÀÈ÷·Á ³» ±â´ë¿¡ ´ëÇØ ÀÌ°ÍÀº SELECT ... INTO OUTFILE ¹®À» ¼öµ¿À¸·Î ½ÇÇàÇÏ¿© ¸í¸í µÈ ÆÄÀÌÇÁ¸¦ outfile·Î Àü´ÞÇÒ ¶§ ¿øÇÏ´Â °á°ú¸¦ Áï½Ã ³ªÅ¸³À´Ï´Ù. ¸í¸í µÈ ÆÄÀÌÇÁÀÇ JNI ³¡ÀÌ Æ®¸®°ÅµÇ¾ú½À´Ï´Ù! ºÒÇàÇÑ ºÎÀÛ¿ëÀ¸·Î ±×·¯³ª ³ª´Â ¶ÇÇÑ MySQL¿¡ÀÖ´Â ¿¹¿Ü¸¦ ¹Þ¾Ò´Ù. SELECT ... INTO OUTFILEÀº ±âÁ¸ ÆÄÀÏÀÌ ¾Æ´Ñ »õ ÆÄÀÏ¿¡¸¸ ¾µ ¼ö ÀÖ½À´Ï´Ù (ÇÑ ¹ø »ý¼º µÈ ¸í¸í µÈ ÆÄÀÌÇÁ´Â ±âÁ¸ ÆÄÀÏ·Î ÀÛµ¿ÇÕ´Ï´Ù). Ã³À½¿¡ ³ª´Â ÀÌ°Í¿¡ ´ëÇØ ¸Å¿ì ½Ç¸ÁÇß´Ù. ±×·¯³ª ¸î °¡Áö ´õ ¼÷°í ÇÑ ÈÄ¿¡ ÇÊÀÚ´Â ½ÇÁ¦ µ¥ÀÌÅÍ°¡ ¾²¿©Áö´Â °ÍÀÌ ¾Æ´Ï¶ó ½ÅÈ£°¡ º¸³» Áö±â¸¸ÇϸéµÇ¹Ç·Î ¿¹¿Ü°¡ Áß¿äÇÏÁö ¾Ê´Ù´Â °ÍÀ» ±ú´Þ¾Ò´Ù. ±×¸®°í ½ÇÁ¦ Æ®¸®°Å¿¡ ¹®À» Ãß°¡ ÇÒ ¶§ SQL ¿¹¿Ü¸¦ ¾ïÁ¦Çϱâ À§ÇØ ºó ³»ºÎ¿¡ °è¼Ó 󸮱⸦ ¼±¾ð ÇÒ ¼ö ÀÖ½À´Ï´Ù.

ÀÌÁ¦ ¸ðµç ºÎºÐÀÌ ¶³¾îÁö¸é¼­ MySQLÀ» »ç¿ëÇÏ¿© Æ®¸®°Å¸¦ µî·ÏÇÏ°í Java¿¡¼­ Æ®¸®°Å Á¶Ä¡¸¦ ¼ö½Å ÇÒ ¼öÀÖ´Â ÀÎÅÍÆäÀ̽º°¡ÀÖ´Â ¸ÚÁø Java ¶óÀ̺귯¸®¸¦ ¸¸µé¾ú½À´Ï´Ù. JAR¿¡´Â ¸í¸í µÈ ÆÄÀÌÇÁ¸¦ ÀÛ¼ºÇÏ°í ºñµ¿±â ÀûÀ¸·Î ÆÄÀÌÇÁ ¿¬°áÀ» ±â´Ù¸®´Â µ¥ ÇÊ¿äÇÑ DLLÀÌ µé¾î ÀÖ½À´Ï´Ù. DLLÀº 32 ºñÆ® ¹× 64 ºñÆ® Windows ¸ðµÎ¿¡ Æ÷ÇԵǸç ÄÚµå´Â ·±Å¸ÀÓ¿¡ "sun.arch.data.model"½Ã½ºÅÛ µî·Ï Á¤º¸¸¦ »ç¿ëÇÏ¿© ÇÊ¿äÇÑ ¹öÀüÀ» È®ÀÎÇÕ´Ï´Ù. Áö±ÝÀº ÆÐÅ°Áö°¡ Windows Àü¿ëÀÌÁö¸¸ ½Ã°£ÀÌ Á» À־ÀÌ Linux¸¦ ȣȯ °¡´ÉÇÏ°Ô ¸¸µé °ÍÀ̹ǷΠ°ÆÁ¤ÇÏÁö ¸¶½Ê½Ã¿À.12-06-2012 ¾÷µ¥ÀÌÆ® : ÆÐÅ°Áö°¡ ÀÌÁ¦ Linux¿¡¼­µµ ÀÛµ¿ÇÕ´Ï´Ù. ±×·¯³ª ¸®´ª½º¿¡¼­´Â ¸í¸í µÈ ÆÄÀÌÇÁ°¡ °°Àº È¿°ú¸¦ °¡ÁöÁö ¾Ê±â ¶§¹®¿¡ ¾à°£ ´Ù¸£°Ô ÀÛµ¿ÇÕ´Ï´Ù; ´ë½Å Àӽà ÆÄÀÏ µð·ºÅ丮¿¡ ¸Þ¸ð¸® ÆÄÀÏ ½Ã½ºÅÛÀ» ¸¶¿îÆ®ÇÏ¿© ¸í¸í µÈ ÆÄÀÌÇÁ ´ëü ÆÄÀÏ·Î ÀÛµ¿ÇÕ´Ï´Ù. ±×·± ´ÙÀ½ inotify¸¦ »ç¿ëÇÏ¿© ûÃë 

¼º´ÉÀ» ±¤¹üÀ§ÇÏ°Ô Å×½ºÆ®ÇÏÁö´Â ¾Ê¾ÒÁö¸¸ ¼Ö·ç¼ÇÀÌ ¸Þ¸ð¸® ³» Åë½ÅÀ» »ç¿ëÇÏ°í Æ®¸®°Å µ¥ÀÌÅÍ°¡ ¼º´É¿¡ Á¦°ø µÉ ¶§±îÁö º°µµÀÇ ½º·¹µå¿¡¼­ ºí·ÎÅ·À» ÀÐÀ¸¹Ç·Î ¼º´ÉÀÌ ÃÖ¼ÒÇÑÀ¸·Î À¯ÁöµÇ¾î¾ßÇÕ´Ï´Ù. Áö±Ý±îÁö ³» ÇÁ·ÎÁ§Æ®¿¡¼­ »ç¿ë¹ýÀÌ ÀÌ°ÍÀ» È®ÀÎÇß´Ù. ´©±º°¡ ½ÇÁ¦ ÀüÈ­ ¹øÈ£°¡ ÀÖÀ¸¸é ÀÇ°ßÀ» ³²°ÜÁÖ¼¼¿ä. 

ÀÌÁ¦ Äڵ带 »ìÆì º¸°Ú½À´Ï´Ù. ´ÙÀ½Àº ±¸ÇöÇؾßÇÏ´Â ÀÎÅÍÆäÀ̽ºÀÔ´Ï´Ù.

import nl.rocksolit.db. *; 

°ø¿ë ÀÎÅÍÆäÀ̽º DatabaseTriggerMonitor 

public void onTrigger (DatabaseTrigger trigger); 
}


´ÙÀ½Àº Æ®¸®°Å¸¦ È°¼ºÈ­ÇÏ´Â ¹æ¹ýÀÔ´Ï´Ù.

import nl.rocksolit.db. *; 

DatabaseTrigger dbTrigger = »õ DatabaseTrigger (conn, "EventLog", DatabaseTrigger.Time.AFTER, DatabaseTrigger.Event.INSERT, this); 

.. 
..


³¡³ª¸é ±×°ÍÀ» ³õÀ» ¼ö ÀÖ½À´Ï´Ù :

.. 
.. 

dbTrigger.drop ();


Æ®¸®°Å´Â <TableName> <Time> <Event>ÀÇ Çü½ÄÀ¸·Î ÀÚµ¿ »ý¼º µÈ À̸§ÀÌ ÁÖ¾î Áö¹Ç·Î À§ÀÇ Æ®¸®°Å´Â MySQLÀÇ onEventLogAfterInsert¶ó´Â À̸§À̵˴ϴÙ. ÀÌ À̸§À» °¡Áø ±âÁ¸ Æ®¸®°Å°¡ »èÁ¦µË´Ï´Ù! ÀÌ°ÍÀº MySQLÀÌ ÀÛ¾÷ ½Ã°£°ú À̺¥Æ® ´ç ÇϳªÀÇ Æ®¸®°Å ¸¸ ÀúÀåÇÒ ¼ö Àֱ⠶§¹®¿¡ ¼öÇàµË´Ï´Ù. Æ®¸®°Å¸¦ ÇØÁ¦Çϱâ À§ÇØ .drop ()¸¦ È£ÃâÇÏ´Â °ÍÀº ¼±Åà »çÇ×ÀÌÁö¸¸ MySQL¿¡¼­ Æ®¸®°Å¸¦ Á¦°ÅÇÏ°í ¸í¸í µÈ ÆÄÀÌÇÁ¸¦ Àß ´ÝÀ» °ÍÀ̹ǷΠÇö¸íÇÕ´Ï´Ù. Java Äڵ尡 Á¾·áµÇ±â Àü¿¡ »èÁ¦µÇÁö ¾ÊÀ¸¸é MySQLÀÇ ¸ðµç Æ®¸®°Å°¡ ¿©ÀüÈ÷ ÀÛ¾÷À» ½ÃÀÛÇÏ¿© ÆÄÀÌÇÁ¿¡ ¾²·Á°íÇÕ´Ï´Ù. ÀÌ·¸°ÔÇصµ ¾Æ¹« °Íµµ ¼Õ»óµÇÁö´Â ¾ÊÁö¸¸ ÀÇ½É ÇÒ ¿©Áö¾øÀÌ ¼º´É¿¡ ¿µÇâÀ»ÁÝ´Ï´Ù. 

Á¦ÇÑ ¹× ¾Ë·ÁÁø ¹®Á¦Á¡ : 
- Windows 7 (x64)¿¡¼­ MySQL 5.1.49 ¹× CentOS 5.4 (x64)¿¡¼­ MySQL 5.0.77À» »ç¿ëÇÏ¿© Å×½ºÆ®
- ÃÖ´ë 64 °³ÀÇ Æ®¸®°Å¸¦ µ¿½Ã¿¡ µî·Ï ÇÒ ¼ö ÀÖ½À´Ï´Ù (Áï, 
Çϳª ÀÌ»óÀÇ Æ®¸®°Å¸¦ ¸ÕÀú È£ÃâÇÏÁö ¾Ê°í »ý¼º) . µ¿ÀÏÇÑ Å×ÀÌºí ½Ã°£ À̺¥Æ® Á¶ÇÕ¿¡ ´ëÇØ µ¿½Ã¿¡ ÇϳªÀÇ Æ®¸®°Å ¸¸ Á¸ÀçÇÒ ¼ö ÀÖ½À´Ï´Ù. ÀÌÈÄÀÇ Æ®¸®°Å¸¦ ÀÛ¼ºÇϸé ÀÌÀüÀÇ Æ®¸®°Å°¡ »èÁ¦µË´Ï´Ù Çϳª 
´Â MySQL°ú µ¿ÀÏÇÑ ÄÄÇ»ÅÍ¿¡¼­ ½ÇÇàµÇ´Â Java ÄÚµå·Î¸¸ Å×½ºÆ®µÇÁö¸¸ Windows¿¡¼­´Â ¾çÂÊ ³¡ÀÌ µ¿ÀÏÇÑ ³×Æ®¿öÅ©¿¡¼­ ½ÇÇàµÇ´Â °æ¿ì¿¡µµ ÀÛµ¿ 
ÇؾßÇÕ´Ï´Ù. ½ÇÁ¦·Î µ¥ÀÌÅͺ£À̽º »ç¿ëÀÚ´Â MySQL FILE ±ÇÇÑ 

ÀÌ ÀÖ¾î¾ßÇÕ´Ï´Ù. ÀÌ ÆÐÅ°Áö´Â Arthur de Vaan / RocksolIT. Æ÷ÇÔ µÈ ¶óÀ̼¾½º¸¦ ÁؼöÇÏ´Â ÇÑ ¹«·á·Î »ç¿ë ¹× ¹èÆ÷ ÇÒ ¼ö ÀÖ½À´Ï´Ù. 
ÆÐÅ°Áö°¡ ½Ã°£ ¹× / ¶Ç´Â µÎÅëÀ» ´ú¾î ÁÖ¾ú°Å³ª µµ¿òÀ̵ȴٸé ÀÛÀº ±âºÎ±Ý ¹× / ¶Ç´Â °¨»çÀåÀ» »ý°¢Çغ¸½Ê½Ã¿À.

jdbtrigger.jar