2

I need to access a MySQL db from my android app. Now all the work is done through

DriverManager.getConnection(url);

and so on. But I have to access the db from multiple threads, so I have to use connection pooling.

Question 1. Is

com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();

the right way of creating the data source?

Question 2. If I write the previous line of code, my app compiles and installs on the device (not emulator) fine, but I get a weird

java.lang.NoClassDefFoundError: com.mysql.jdbc.jdbc2.optional.MysqlDataSource`

, that I can't catch with a try/catch handler:

try
{
com.mysql.jdbc.jdbc2.optional.MysqlDataSource a = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
}
catch (Exception e)
{
I don't get here. The app just crashes, as if I had no try/catch block.
}

Question 3. I copied mysql-connector-java-5.1.20-bin.jar to the device and wrote the following code:

try
{
final String str = Environment.getExternalStorageState();
final File sd = getActivity().getExternalFilesDir(null);
final File file = new File(sd, "mysql-connector-java-5.1.20-bin.jar");

boolean b = file.exists();

final URLClassLoader cl = URLClassLoader.newInstance(new URL[] {file.toURI().toURL()} );
cl.loadClass("com.mysql.jdbc.jdbc2.optional.MysqlDataSource"); //$NON-NLS-1$
}

catch (Exception e)
{
new AlertDialog.Builder(getActivity())
.setMessage(ObjectConverter.throwable2String(e))
.show();
}

The file is found but

cl.loadClass()

fails with

java.lang.NullPointerException
at java.net.URLClassLoader.getPermissions(URLClassLoader.java:614)
at java.security.SecureClassLoader.getPD(SecureClassLoader.java:140)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:93)
at java.net.URLClassLoader.access$600(URLClassLoader.java:55)
at java.net.URLClassLoader$URLJarHandler.createClass(URLClassLoader.java:364)
at java.net.URLClassLoader$URLJarHandler.findClass(URLClassLoader.java:303)
at java.net.URLClassLoader.findClass(URLClassLoader.java:748)
at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
at ru.mypkg.myapp.func(myapp.java:367)
at android.view.View.performClick(View.java:3511)
at android.view.View$PerformClick.run(View.java:14110)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)

Any help greatly appreciated.

RobinBobin
  • 555
  • 7
  • 17
  • 1
    Please wrap your database in a Web service and use that from Android. JDBC is not designed for unreliable network connections of the kind that you find with mobile devices. – CommonsWare May 13 '12 at 00:01
  • Thanks a lot for the answer! I already thought about using HTML for requests and responses, but currently it's impossible, I guess, 'cause we have no time to do that. Why are network connections unreliable if we speak about mobile devices? What's wrong about JDBC that makes it unsuitable for such tasks? And, most important, can I somehow establish a pooled connection to MySQL from Android? – RobinBobin May 13 '12 at 11:37
  • 1
    "Why are network connections unreliable if we speak about mobile devices?" -- mobile devices are mobile. Users fall off of WiFi and fail over to mobile data, or flip back again. Users meander into places where there is lousy mobile data signal. And so on. "What's wrong about JDBC that makes it unsuitable for such tasks?" -- JDBC is ~15 years old and is designed around a stable LAN connection (I wouldn't use it over the Internet, either). "can I somehow establish a pooled connection to MySQL from Android?" -- I have no idea and, if they worked for me, I would fire anyone who tried. – CommonsWare May 13 '12 at 12:13
  • Certainly, it's like this. But in the context of the app I'm developing it's a bit different. We organize a WiFi network and state that our app (being only a part of a bigger complex) works only in its presence (and the customers agree with that). Do I get you right about wrapping the DB in a Web-service? You mean the app sending requests as HTML and getting responses the same way? – RobinBobin May 13 '12 at 14:24
  • "We organize a WiFi network and state that our app (being only a part of a bigger complex) works only in its presence (and the customers agree with that)." -- um, OK. "You mean the app sending requests as HTML and getting responses the same way?" -- no, I mean a Web service -- http://en.wikipedia.org/wiki/Web_service REST, and to a lesser extent SOA, works best on Android, simply because Android lacks a built-in SOAP endpoint. – CommonsWare May 13 '12 at 14:36
  • Thank you very much for the detailed answers! I'm afraid, I know nothing about web-services (hopefully, yet), but I'll do my best. – RobinBobin May 13 '12 at 17:11

1 Answers1

2

This question is 6 years old and Mark is correct that, in almost every scenario you can think of, JDBC on Android is about as sensible as trying to use a toaster in the bath. However, sometimes we do things because we can and not because we necessarily should, and today I had a justifiable reason to want to do this (for a specific very niche app running in an unusual environment), which is how I found this question.

Addressing the NoClassDefFoundError first, the reason it isn't caught by the catch block is because it's an Error not an Exception. Both Error and Exception inherit from Throwable, so you could catch that instead:

catch (Throwable t)
{
    // This will catch NoClassDefFoundError
}

I believe that it's not MySqlDataSource that it can't find, but one of the classes or interfaces that it depends on - in my case it was javax.naming.Referenceable. Android doesn't provide the javax.naming package so trying to use the pooling features of the Connector/J JDBC driver for MySQL isn't going to get you very far (you could try to provide the missing dependencies but that road likely leads to madness).

Instead you'll probably have more luck with a third-party connection pool implementation. There are various Java libraries for doing this. Some of them will work on Android. One that I have verified does work is HikariCP. There are instructions for configuring it here and, specifically for MySQL, here.

Dan Dyer
  • 53,737
  • 19
  • 129
  • 165
  • 1
    Thanks for sharing your approach and thoughts on the subject. I think it's really valuable for guys who wan't to "swim in a bath with a toaster" :) . P.S. I hope today I would be able to understand why NoClassDefFound doesn't get caught :) . – RobinBobin May 26 '18 at 11:47
  • Can you please tell me how did you get HIkariCP to work on android? I getting class-not-found and other dependencies issues. I am trying to use it within my-jar I'm importing to Android Studio project. – sharonooo Sep 30 '18 at 20:11
  • @sharonooo Which classes can't it find? I added the Hikari dependency to my Gradle file and also included the MySQL Connector JAR file in my project. I've also got slf4j-android in my Gradle dependencies, which I think is due to Hikari. – Dan Dyer Oct 01 '18 at 12:49
  • @DanDyer I'm getting: `Caused by: java.lang.ClassNotFoundException: Class io.prometheus.client.Collector not found` I created a JAR with all dependencies and I'm importing this jar to android studio with gradle. I am trying to use HakiriCP with H2 database. One thing I noticed: HakiriCP has `provided` dependencies which is not added to the class path and this what cause the error. – sharonooo Oct 02 '18 at 06:52
  • Also tried to import io.prometheus to androids project gradle file. and there is another error: `Could not find class 'javax.naming.InitialContext', referenced from method com.zaxxer.hikari.HikariConfig.getObjectOrPerformJndiLookup` – sharonooo Oct 02 '18 at 10:27
  • @sharonooo I got it working with MySQL, it may be that the H2 driver isn't very Android friendly. I don't know what that Prometheus dependency is. As mentioned above I had similar issues with `javax.naming` when trying to use the MySQL driver's pooling functionality, but avoided it when using Hikari. – Dan Dyer Oct 02 '18 at 16:48
  • @DanDyer can you tell me please which version of Hakiri and MySQL are you using? btw thanks – sharonooo Oct 02 '18 at 20:43
  • @sharonooo `com.zaxxer:HikariCP-java7:2.4.13` in my Gradle file and `mysql-connector-java-5.1.45-bin.jar` in my libs directory. I'm setting the driver class name in `HikariConfig` to `com.mysql.jdbc.Driver`. – Dan Dyer Oct 03 '18 at 09:55