How to intercept and modify SQL of PreparedStatement with Spring? - prepared-statement

Is there a central location in the JDBCTemplate (or related) where SQL manipulations can be performed immediately before they are sent to the DB?
I want to prepend a comment-line to each and every SQL statement that gets issued to the RDBMS.
Hope there is a dedicated extension point. Otherwise, I would need to write my own class that inherits from JDBCTemplate and adds my custom logic, which I want to avoid.

Is there a central location in the JDBCTemplate (or related) where SQL manipulations can be performed immediately before they are sent to the DB?
Yes, the DataSource.
With datasource-proxy you can intercept queries on DataSource level using a custom QueryTransformer:
private static class MyQueryTransformer implements QueryTransformer {
#Override
public String transformQuery(TransformInfo transformInfo) {
String query = transformInfo.getQuery();
// transform query
return query;
}
}
and supplying it into ProxyDataSourceBuilder:
ProxyDataSourceBuilder.create()
...
.queryTransformer(new MyQueryTransformer())
...
See also datasource-proxy-examples

Related

How to profile Entity Framework activity against SQL Server?

It's easy to use SQL Server Profiler to trace stored procedures activity. But how to trace SQL queries issued by LINQ via Entity Framework? I need to identify such queries (LINQ code) that consume a lot of time, are called most frequently and therefore are the first candidates for optimization.
Add this key to your connection string:
Application Name=EntityFramework
And filter by this in Profiler
Adding #ErikEJ's answer : if you are using .net Core, so you are using EFCore. There are no Database.Log property. You should use OnConfiguring override of your DbContext class and then
optionsBuilder.LogTo(Console.WriteLine);
Sample :
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.LogTo(Console.WriteLine);
}
}
I've found useful DbContext.Database.Log property.
MSDN article Logging and Intercepting Database Operations
The DbContext.Database.Log property can be set to a delegate for any method that takes a string. Most commonly it is used with any TextWriter by setting it to the “Write” method of that TextWriter. All SQL generated by the current context will be logged to that writer. For example, the following code will log SQL to the console:
using (var context = new BlogContext())
{
context.Database.Log = Console.Write;
// Your code here...
}
What gets logged?
When the Log property is set all of the following will be logged:
The approximate amount of time it took to execute the command. Note that this is the time from sending the command to getting the result object back. It does not include time to read the results.
SQL for all different kinds of commands. For example:
Queries, including normal LINQ queries, eSQL queries, and raw queries from methods such as SqlQuery
Inserts, updates, and deletes generated as part of SaveChanges
Relationship loading queries such as those generated by lazy loading
Parameters
Whether or not the command is being executed asynchronously
A timestamp indicating when the command started executing
Whether or not the command completed successfully, failed by throwing an exception, or, for async, was canceled
Some indication of the result value

Insert data into external MSSQL database through Yii Framework

I'm using Yii Framework to create my project. I need to export some data from MySQL (my project) to an external Microsoft SQL server which is on the same network.
Basically, the user needs to click on a button (which will do the export-insert) in my view and the results should be displayed - Success (if the query has been successful) or Failure (if something went wrong).
The results part is quite easy as I'll be using 'setFlash' to display the appropriate message but I want to know how to insert data into an external database through Yii.
Do you have any idea how this can be done?
Well, I agree with #SuVeRa on the first part of defining two db instances in the config.php but i don't think the sql Commands part is necessary (Plus i hate writing sql :D )
Instead you can do:
class SomeModel extends CActiveRecord
{
...
// Override the getDbConnection() function to use the ms sql db connection
public function getDbConnection()
{
return Yii::app()->ms_sql_db_connection; // The name of the connection in config.php
}
public function transfer()
{
// Here you can do all the transferring logic using normal Yii Active Record functions
}
}
Check out the docs on getDbConnection().

NHibernate with custom sql-insert vs catching procedure raised exceptions and out params

I have mapped classes with custom sql (insert, delete, update) through procedure calls. But, I noticed that when my insert procedure fails raising exception, the GenericAdoException from NHibernate doesn't have my message raised from the procedure.
But, all raised exceptions from procedures for delete and update is catched well, only the insert procedure hasn't its exception message catched.
Is that a limitation or a bug of NHibernate 3.2.4 when we use "native" generator for ids combined with custom sql ?
I'm searching also ways to get some out parameters from that procedures like a timestamp to each event (insert, delete and update), the timestamp is generated inside procedures.
EDIT: OUT PARAMs - I found the "generated" option over properties mapping options which we can ask to NHibernate to get params from procedures. This means that these properties have genarated values. So I tried to use generated="always" and works for insert, update and delete operations. Example: <property name="MyProp" generated="always"/>
I found that sql server driver doesn't put the messages raised by stored procedures into the SqlException when you run these stored procedures with ExecuteReader(). On the other hand NHibernate executes the custom sql-insert with ExecuteReader() (I debbuged its source code) and I guess it's right and necessary to get the key when it's mapped with native (or identity), my case.
Well, and now what to do ? I found also (hard to found) that the SqlConnection has a event called "InfoMessage" in which you can receive (catch) all messages sent from your stored procedures (raiserror). Now this is possible to "catch" these messages, but how to make them cross NHibernate core and be received by our application when we insert something session.save() ?
Altough we have access to session and so to the connection (SqlConnection) the messages was already lost, because them are only received by the delegate assigned to the event SqlConnection.InfoMessage before of its occurrence.
To solve this, I tried two approaches:
In the first I projected a way to register the delegate inside DriverConnectionProvider.GetConnection() and this delegate would store the messages on the thread context associating it with the connection, so these messages could be getted later.
In the second and the one choosed, I implemented IDbConnection and IDbCommand wrapping inside them the SqlConnection and SqlCommand (but I think the NHibernate has a bug because in some places it references DbConnection instead IDbConnection - like in ManagedProviderConnectionHelper, so I had to extend from DbConnection and DbCommand instead).
Inside my CustomSqlConnection I register the delegate and store the messages for later use.
This is working ! Work as standalone driver (ADO) either as a NHibernate driver.
The idea is:
public class CustomSqlConnection : DbConnection, IDbConnection {
private SqlConnection con;
private StringBuilder str = new StringBuilder(0);
public CustomSqlConnection() {
con = new SqlConnection();
con.InfoMessage += OnInfoMessage;
}
private void OnInfoMessage(object sender, SqlInfoMessageEventArgs e) {
if (str.Length > 0) {
str.Append("\n");
}
str.Append(e.Message);
}
public string FetchMessage() {
string msg = Message;
str.Clear();
return msg;
}
...
...
}
EDIT: The hard step is to implement all operations from DdConnection and Dbcommand, repassing the call to the sql instance (look the field con above), so:
...
public override void Open() {
con.Open();
}
...

Logback dbAppender Custom SQL

Is there a way to change the tables that logback writes its data to using the dbAppender, It has three default tables that must be created before using dbAppender, but I want to customise it to write to one table of my choosing. Something similar to Log4J where I can specify the SQL that gets executed when inserting the log to the database.
Tomasz, maybe I'm missing something but I don't see how just using custom DBNameResolver could be the answer to what Magezy asked. DBNameResolver is used by DBAppender via SQLBuilder to construct 3 SQL insert querys - via DBNameResolve one can only affect names of tables and columns where data will be inserted, but can not limit inserting to just one table, not to mention that by just implementing DBNameResolver there are no means to control what actually gets inserted.
To match log4j's JDBCAppender IMO one has to extend logback's DBAppender, or DBAppenderBase, or maybe even implement completely new custom Appender.
The easiest way for me was to make an appender from scratch. I'm appending to a single table, using Spring JDBC. It works something like this:
public class MyAppender extends AppenderBase<ILoggingEvent>
{
private String _jndiLocation;
private JDBCTemplate _jt;
public void setJndiLocation(String jndiLocation)
{
_jndiLocation = jndiLocation;
}
#Override
public void start()
{
super.start();
if (_jndiLocation == null)
{
throw new IllegalStateException("Must have the JNDI location");
}
DataSource ds;
Context ctx;
try
{
ctx = new InitialContext();
Object obj = ctx.lookup(_jndiLocation);
ds= (DataSource) obj;
if (ds == null)
{
throw new IllegalStateException("Failed to obtain data source");
}
_jt = new JDBCTemplate(ds);
}
catch (Exception ex)
{
throw new IllegalStateException("Unable to obtain data source", ex);
}
}
#Override
protected void append(ILoggingEvent e)
{
// log to database here using my JDBCTemplate instance
}
}
I ran into trouble with SLF4J - the substitute logger error described here:
http://www.slf4j.org/codes.html#substituteLogger
This thread on multi-step configuration enabled me to work around that issue.
You need to implement ch.qos.logback.classic.db.names.DBNameResolver and use it in the configuration:
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<dbNameResolver class="com.example.MyDBNameResolver"/>
<!-- ... -->
</appender>
<appender name="CUSTOM_DB_APPENDER" class="com.....MyDbAppender">
<filter class="com......MyFilter"/>
<param name="jndiLocation" value="java:/comp/env/jdbc/....MyPath"/>
</appender>
And your java MyDbAppender should have a string jndiLocation with setter.
Now do a jndi lookup (see the solution answered in Oct 17 '11 at 16:03)

Entity framework am I using to SQL CE or SQL Server?

I'm using EF with 2 databases - with SQL CE and SQL Server.
Is there a way to know which connection type is used at runtime? I mean, if I have only ObjectContext in some place (already initialized with some connection string), can I get the database type from it (is it Compact or SQL Server at the moment)?
Thanks
You can check the Connection Property, which should return an EntityConnection; from there you must check its StoreConnection which will be the "real" database connection.
From there, you can either check the ConnectionString, which will tell you the provider, or simply check the type of the provider connection itself with is or GetType. If it's SQL Server it will be a SqlConnection, and if it's SQL CE it will be a SqlCeConnection.
This looks like an ugly hack because it is; if you're looking for a way to do this without an ugly hack, don't bother - the ObjectContext is explicitly designed not to leak any information about the connection unless you know exactly what to ask for. By contrast, here's all the hoops you would have to jump through to check it via the app config:
static string GetProviderName(ObjectContext context)
{
string entityConnectionString = GetEntityConnectionString(context);
return !string.IsNullOrEmpty(entityConnectionString) ?
GetProviderConnectionString(entityConnectionString) : null;
}
static string GetEntityConnectionString(ObjectContext context)
{
var match = Regex.Match(context.Connection.ConnectionString,
#"name=(?<name>[^;]+)", RegexOptions.Compiled);
string connectionStringName = match.Success ?
match.Groups["name"].Value : null;
return ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
}
static string GetProviderConnectionString(string entityConnectionString)
{
var match = Regex.Match(entityConnectionString,
#"provider=(?<provider>[^;]+)", RegexOptions.Compiled);
return match.Success ? match.Groups["provider"].Value : null;
}
Once any solution starts to involve regexes, I tend to look for a straighter path, and in this case, that's the type casting you say you'd prefer not to use. Pick your poison.
Be careful how you use either of the above approaches. EF4 is designed around persistence ignorance and you should be trying to avoid any logic that's specific to the connection type, because you really have no idea how it will be configured (maybe tomorrow it will be an Oracle connection). I believe that the provider-specific code resides mainly in the QueryProvider.

Resources