Handle multiple JDBC drivers from the SAME VENDOR - jdbc

I came across with a big problem yesterday. In my current project I use ojdbc6 implementation of Oracle's JDBC for a connection, but also I would need to handle for example oracle 8 databases, which is totally impossible with this JAR.
You would say that I should use ojdbc14 for example which was true for some tests, but lets assume that later on I will need to handle 2 kind of databases from the same vendor, but we know that there is no existing implementation for BOTH and I need to have those simultaneously loaded. Same interface (and well, not just same interface, same class-structure, just different implementation inside!), same URL connection prefix -> JDBC connection will use one driver, but I cannot load multiple of them. So what now?
My first idea was to load the JARs with different classloaders, maybe I could load the same package structure with the same classes separated from each other? I don't really think so, maybe that was a silly idea of mine. This could be also a general problem later not with just JDBC drivers, so even if you cannot answer to my question but you know what is lacking here please tell me
Even if I could do a separate loading of class implementations of the same class names, how I can tell to the DriverManager when creating a connection to use the EXACT driver instead of finding one based on the connection url's prefix? (where I mean jdbc:oracle:thin for example).
I feel like a total dumb now because I think this is not an entirely extraordinary idea to handle in the Java world, BUT I totally don't know how to handle.
Thanks for y'all in advance

You actually have a couple of options:
You can try to load the drivers from different class loaders. That will work if you need only pure JDBC in your application. I doubt that you will get Hibernate to work with such a setup.
Eventually, you will have to run code where you will need to see instances from both classloaders and here, you will get ClassCastExceptions (two classes with the same full qualified name are different when they were loaded from different class loaders).
You can split your application into two. The second one would a small server which takes commands from your original app and translates those into JDBC for the database. The small server talks to Oracle 8 while your app only talks to one database.
This approach would allow you to keep the two concerns completely separate but you won't be able to run joins on the two databases.
You can link the old Oracle 8 database in your new database using CREATE DATABASE LINK. That makes the old tables visible as if they were part of the new database. You app only talks to one DB and Oracle handles the details internally.
Maybe Oracle 8 is too old for this to work but I'd definitely give it a try.
Oracle JDBC drivers are more compatible that you might expect. When you say "which is totally impossible with this JAR", did you try it? I used an Oracle 10 driver to connect to Oracle 7 in the past. Not every feature was supported but I could run the standard queries and updates.

#jdbc.properties
oracle.driver=oracle.jdbc.OracleDriver
oracle.url=jdbc:oracle:thin:#//localhost/xe
oracle.user=scott
oracle.password=tiger
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost/sales
mysql.user=root
mssql.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
mssql.url=jdbc:sqlserver://192.168.1.175;databaseName=sales
mssql.user=dbviewer
mssql.password=dbviewer
And then read the properties file:
class QueryTest2 {
public static void main(String[] args) throws Exception{
Properties settings = new Properties();
FileInputStream fin = new FileInputStream("jdbc.properties");
settings.load(fin);
fin.close();
String dvr = settings.getProperty(args[0] + ".driver");
String url = settings.getProperty(args[0] + ".url");
String usr = settings.getProperty(args[0] + ".user");
String pwd = settings.getProperty(args[0] + ".password");
Class.forName(dvr);
Connection con = DriverManager.getConnection(url, usr, pwd);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select pno,price,stock from products");
while(rs.next()){
System.out.printf("%d\t%.2f\t%d%n", rs.getInt(1), rs.getDouble(2), rs.getInt("stock"));
}
rs.close();
stmt.close();
con.close();
}
}

Related

Spring Data Repository - Paging large data sets (EclipseLink)

I am using Spring Data with EclipseLink JPA to do server side pagination on a database result set. I have everything working and I get the expected paged results, but I noticed performance suffering on large data sets (several million rows). It is taking about 5 minutes to return a page of 20 results. Perhaps this is to be expected, but what concerned me was the query output.
My log output:
SELECT COUNT(filename) FROM document
SELECT filename, datecaptured, din, docdate, docid, doctype, drawer, foldernumber, format, pagenumber, tempfilename, userid FROM document ORDER BY din ASC
I would understand that in order to page, Spring would need to know the max row count, so the first query makes sense.
The second query is pulling the entire database when I specifically only asked for 20 results with a 0 offset (page).
Does Spring/EclipseLink/JPA in fact grab the entire data set and then only return the subset paged request?
If that is the case, how should I modify my repository class to be more efficient?
My test case:
#Test
public void getPagedDocumentsTest() throws IOException {
Page<Document> requestedPage = documentRepository.findAll(new PageRequest(0, 20, Sort.Direction.ASC, "din"));
Assert.assertNotNull("Page is null", requestedPage);
Assert.assertNotNull("Page is empty", requestedPage.getContent());
List<Document> documents = requestedPage.getContent();
LOG.info("{}", documents);
LOG.info("{}", documents.size());
}
My repository class:
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.example.data.model.Document;
#Repository
public interface DocumentRepository extends PagingAndSortingRepository<Document, String> {
}
Edit - per #Chris's suggestion
Tried adding the platform to my properties, but it didn't make a difference:
eclipselink.weaving=static
eclipselink.allow-zero-id=true
eclipselink.target-database=SQLServer
eclipselink.logging.level=FINE
Also tried adding it to my configuration (I'm using Java Config):
#Bean
public LocalContainerEntityManagerFactoryBean entityManager() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("ExampleUnit");
factory.setPackagesToScan("com.example.data.model");
EclipseLinkJpaVendorAdapter eclipseLinkVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkVendorAdapter.setDatabase(Database.SQL_SERVER);
eclipseLinkVendorAdapter.setDatabasePlatform("SQLServer");
factory.setJpaVendorAdapter(eclipseLinkVendorAdapter);
factory.setDataSource(dataSource());
factory.setJpaProperties(jpaProperties());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
Looks like the platform is set correctly.
[EL Config]: connection: 2015-08-06 12:04:05.691--ServerSession(686533955)--Connection(1896042043)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>SQLServerPlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
But neither helped. The SQL query output remained the same as well.
Edit
Found a related question with a similar answer from #Chris:
EclipseLink generated SQL doesn't include pagination
EclipseLink 2.5 source that I checked I believe has support for database level filtering built into the following database platform classes:
DB2Platform
DerbyPlatform
FirebirdPlatform
H2Platform
HANAPlatform
HSQLPlatform
MySQLPlatform
OraclePlatform
PostgreSQLPlatform
SymfowarePlatform
Each of these override the printSQLSelectStatement method to take advantage of their respective database features to allow filtering in the SQL itself. Other platforms will need to use JDBC filtering, which depend on the driver to restrict rows - they may be able to optimize queries, but it is driver specific and I believe it is why your query takes longer then you desire.
I don't know SQLServer well enough to say what equivalent functionality it has that can be used within the SQL, but if you find it, you would need to create a SQLServerPlatform subclass, override the printSQLSelectStatement method as is done in the above classes, and then specify that platform class be used instead. Please also file a bug/feature to have it included in EclipseLink.
Other options are described here:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination
One thing you should consider is whether you actually need to know the number of pages / total number of elements. If you are returning a page from a result set that has milions of elements, chances are your users will not be interested in looking through all those pages either way :). Maybe your front end shows the data in a an infinite scroll that just needs to know, if there are any more pages, instead of number of pages.
If any of those cases apply to you, you should consider returning a Slice instead of a Page, as in:
public Slice<MyClass> findByMyField(..);
This way, instead of doing the expensive Count, Spring Data will just ask for one more element than you originally wanted. If that element is present, the Slice will return true from the hasNext method.
Where I work we recently used Slices for several large data sets and with the right indexes (and after clearing the database cache :) we have seen some really significant gains.

Security: Session Identifier Not Updated in tcl

I'm working on open-source application "Project-Open" and during the scanning I got the following vulnerability:
[Medium] Session Identifier Not Updated
Issue: 13800882
Severity: Medium
URL: https://<server_name>/register/
Risk(s): It is possible to steal or manipulate customer session and cookies, which might be used to impersonate a legitimate user,allowing the hacker to view or alter user records, and to perform transactions as that user
Fix: Do not accept externally created session identifiers
though the fix is mentioned but it is not sufficient for me to understand it completely.please guide me how should I remove this.Also let me know if any further details are needed to understand the question.
The project source code is in tcl
I found the following code which does the same but it's in java.
public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException {
// get the current session
HttpSession oldSession = request.getSession();
// make a copy of the session content
Map<String,Object> temp = new ConcurrentHashMap<String,Object>();
Enumeration e = oldSession.getAttributeNames();
while (e != null && e.hasMoreElements()) {
String name = (String) e.nextElement();
Object value = oldSession.getAttribute(name);
temp.put(name, value);
}
// kill the old session and create a new one
oldSession.invalidate();
HttpSession newSession = request.getSession();
User user = ESAPI.authenticator().getCurrentUser();
user.addSession( newSession );
user.removeSession( oldSession );
// copy back the session content
for (Map.Entry<String, Object> stringObjectEntry : temp.entrySet()){
newSession.setAttribute(stringObjectEntry.getKey(), stringObjectEntry.getValue());
}
return newSession;
}
P.S. I'm newbie in TCL.
please let me know if you need any further explanation.
There is a fix in OpenACS 5.9 that addresses your scanning reports. Please see the following discussion on OpenACS.org for reference.
http://www.openacs.org/forums/message-view?message_id=5332821
The problem that the OWASP report is talking about is the inability to migrate a session to use a new ID, making it easier for an attacker to discover the ID and reuse it. The protection against this is to change the session ID from time to time (no, I don't know how often!) and that Java code is involved in doing just that.
A session is represented as a token stored in the browser, usually in a cookie (and this is what cookies are designed to do). That token is then used to look up the database record corresponding to the session, which holds serializations of the key/value mappings in the session. It's a simple mechanism, but very powerful. The Java code for doing all this will be fairly complex behind the scenes because of the serialization, etc., but Tcl values are (usually, and always for built-in types) naturally serializable and so should prove much less of a problem in this; copying a session to a new key could be done without having to deserialize in the first place.
The exact code for doing this depends on the framework in use. I don't know what ]project-open[ uses, so that's as far as we can drill right now. You need to talk to other people actually working on PO…
For all that, the best way would be to make the key given to clients not be the primary key, so that you can change the session key without having to delete things. Just have a session key column (with an index!) and you'll be able to make things work fine. This is a more sophisticated approach though; it might not be practical to implement in your environment..

Refresh LINQ db-connection object

My collegue helped me with starting programming in c# although I had no experience but I like it. All went well until I came across some problems we both can't fix. He uses SQL himself but started me up with LINQ.
To do a LINQ-query I use this object : _oDBConnection (in clsApplication.cs)
So when opening the programm this object is built. But it creates some problems:
When saving a new object (putting data into table), I cannot load those values with a query. I need to restart the programm.
When running 2 instances of the programm, one is not getting the latest values when changed in the other (but it is showing the new ones but not the changed ones!)
According to these problems I can only conclude that when I call clsApplication._oDBConnection.tblTAble a second time it is not relinking again to the db but is giving me the old db-states back.
This is the code he built:
public static DBReservationDataContext _oDBConnection;
private static frmMain _fMain;
public clsApplication()
{
Thread.CurrentThread.Name = "main";
clsErrorLog.ErrorLocation = "C:\\Software\\ErrorLog";
clsErrorLog.setPassword("*****");
clsErrorLog.LockApplication += new clsErrorLog.dLockApplication(lockApplication);
_oDBConnection = new DBReservationDataContext();
_fMain = new frmMain();
_fMain.Show();
}
What can I do to fix this problem?
Example:
although present in the database, it crashes on this query because the entity with id == iID is not found. But the iID is correct and it does exist in the database. The query will work after closing and restarting the programm. Then the clsApplication is called again.
public clsReservationDetail(int iID)
:this()
{
_oReservationDetail = (from oReservationDetailQuery in clsApplication._oDBConnection.tblReservationDetails
where oReservationDetailQuery.ID == iID
select oReservationDetailQuery).First();
}
thx in advance
Your data context will have a Refresh method which will clear any cached results, and should allow your query to complete with no problems
The static keyword makes it so that you have one reference per AppDomain. That is the wrong way to use DataContext instances.
Each instance of DataContext tracks the objects it has seen. This is for consistency. If you get a Customer instance with CustomerID = 4 from one query, you should get the same Customer instance from another query that returns the CustomerID = 4 record.
If you want to see the changes in the database, you must
1) Tell the datacontext to stop tracking changes. This must be done before the first query and makes the datacontext instance unable to SubmitChanges (since it can't track them anymore).
OR
2) Tell the datacontext to Refresh each instance you suspect has changed. If you do that, you should specify how resolve the conflict between your local changes and the remote changes - the simplest way of resolving this conflict is to have no local changes.
OR
3) (The right way) Make a new DataContext instance and load the record with that!
Also note: Since DataContext implements IDisposable, you are required to call Dispose when you are done with each instance even when exceptions occur. The using block is a good way to get that to happen.

Linq-To-Entities connection strings for for multiple edmx files

I have a dot net 4.0 DAL library project that contains three edmx files. I chose to split my data model in three files because there are many tables in my db, and from what I've read here at Stackoverflow it seemed best practice to do so.
But now I have three connection string entries in my App.config that basically uses the same database connection. Can I somehow get back to the old ado.net style with only one connection string in my config files?
Thanks,
Jens
In your code you could use the EntityConnectionStringBuilder to build the three separate connection strings from a 'regular' connection string that's stored in your config file.
You would get something like:
string providerString = <load your connection string>;
// Initialize the EntityConnectionStringBuilder.
EntityConnectionStringBuilder entityBuilder =
new EntityConnectionStringBuilder();
//Set the provider name.
entityBuilder.Provider = providerName;
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/AdventureWorksModel.csdl|
res://*/AdventureWorksModel.ssdl|
res://*/AdventureWorksModel.msl";
You can create three EntityConnectionStrings and change the MetaData property on each one to point to your Model.
But be aware that this would lead to a hardcoded part of your connection string in code.

Moving SP-Linq queries from test site to production site

I see articles on using SPMetal to generate the .cs file that allows LINQ to work properly. The file I'm talking about inherits from the Microsoft.SharePoint.Linq.DataContext class. How can I use LINQ without recompiling on my production environment, since I would need to regenerate this file using SPMetal on my production environment? I suspect the answer is going to be "can't do it".
I guess I'll use a CAML query instead unless there is some easier way to use LINQ that I am missing.
If the objective is just to query lists using LINQ and you want to avoid such recompilations, do not use SPMetal.
LINQ can be directly used on SPListItemCollection
e.g.
var FindCustomer = from SPListItem Item in Customers.Items
where Item["Orders"] as int == 5
select Item;
//or select new{Title = Item["Title"]}
This does not have hard coded entities but is more flexible. And as long as your list column names remain same, code can be deployed on any environment even if other lists are changing.
Also you can choose to retrieve few chosen field's data instead of retrieving data of all the fields every time.
There is no problem I guess. Personally I have been using Linq for good amount of time. I never generated the cs specifically for production. Is your site different across environments?
Im not sure if I'm missing the point or not, but the DataContext object takes the URL as apart of the constructor, so you should retrieve the URL from config somewhere E.g. database
DataContext teamSite = new DataContext("http://MarketingServer/SalesTeam");
OR use the SPContext object, if your code has a SharePoint context. E.g. in a web part
DataContext teamSite = new DataContext(SPContext.Current.Web.Url);

Resources