Cómo configurar la conexión a SQLServer mediante JDBC (NetBeans, Java 8, SQLServer Express 2016, Windows 10…)

Standard

Nunca antes, luego de haber aprendido cómo conectarme a distintas bases de datos mediante JDBC, me había costado tanto una como lo fue con SQLServer (casi 15 minutos). Gracias a Google, Stack Overflow y conocimiento previo salió medianamente rápido, pero vamos, que a esta gente le gusta complicar a veces las cosas.

Archivo de propiedades de conexión (config.properties)

Ya sabemos que es de muy mala educación, y prohibido además, hard-codear valores o atributos que pueden cambiar en el tiempo de una computadora, una red, etc… a otra; entre ellos las propiedades de conexión a una base de datos. Es por eso, que lo ideal es colocarlo en un archivo de propiedades como los que estamos acostumbrados a hacer:

#Ubicado en src\res\config.properties
db.url=jdbc:sqlserver://servidor\\SQLEXPRESS;databaseName=nombre_bd;integratedSecurity=true
db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
db.user=usuario
db.password=clave

Respecto a los valores db.url y db.driver, recordemos que cada manejador de base datos  (o DBMS, como quieran llamarlo) tiene su forma de establecer estos valores, los que les indico funcionan con SQLServer Express 2016; acá mucha mas información respecto a cómo formar la url.

Y una clase en Java sencilla que lo lea:

package th.control.db;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

/**
 *
 * @author jescobar
 */
public final class DBConnection {
 static InputStream inputStream;
 static Connection conn;
 
 public static void connect() throws Exception {
 inputStream = Class.forName("th.control.db.DBConnection").getClassLoader().getResourceAsStream("config.properties");
 
 Properties prop = new Properties();
 
 prop.load(inputStream);
 
 
 Class.forName(prop.getProperty("db.driver"));
 conn = DriverManager.getConnection(
 prop.getProperty("db.url"), 
 prop.getProperty("db.user"), 
 prop.getProperty("db.password"));
 
 System.out.println("Me conecté!!!");
 }
}

Todo bien, compila al pelo, pero al ejecutar un indeseado java.lang.NullPointerException te aparece en la línea donde intentas cargar el archivo de propiedades mediante el método prop.load(inputStream).

run:
Exception in thread "main" java.lang.NullPointerException
 at java.util.Properties$LineReader.readLine(Properties.java:434)
 at java.util.Properties.load0(Properties.java:353)
 at java.util.Properties.load(Properties.java:341)
 at th.control.db.DBConnection.connect(DBConnection.java:26)

El punto es que ni NetBeans ni Eclipse te van a decir dónde colocar el archivo para que sea leído, debes especificar en cada IDE que rutas incluir en el classpath. En NetBeans una solución sencilla es moverlo al directorio src.

#Ubicado en src
db.url=jdbc:sqlserver://servidor\\SQLEXPRESS;databaseName=nombre_bd;integratedSecurity=true
db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
db.user=usuario
db.password=clave

Ahora tu problema va a ser otro…

Colocando donde es el Driver de conexión

Yo estoy partiendo de que ya te habías descargado el driver JDBC de Microsoft, bien sea el sqljdbc41.jar o el sqljdbc42.jar (clic acá si no lo has hecho) para conectarte con su BD, creaste un directorio lib en el proyecto, y ahí lo metiste. Pero al tratar de ejecutar, recibes otro mensaje de que no vas…

run:
Exception in thread "main" java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver
 at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Class.java:264)
 at th.control.db.DBConnection.connect(DBConnection.java:29)

Y es que tienes que decirle a NetBeans donde estas colocando tus librerías externas. Algo así logras presionando sobre el botón “Add JAR/Folder” y especificando la ruta que visualizas en la imagen.

002

Y con lo anterior, al tratar de ejecutar… Cambiamos de problema! Uno mas de no sabemos cuantos mas… Pero lo que si sabemos, que como todo, es que en algún momento ya no habrán.

Ahora la librería que colocamos nos está diciendo que no logra conectarse con el servidor SQLServer…

run:
Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: No se pudo realizar la conexión TCP/IP al host localhost, puerto 1433. Error: "Connection refused: connect. Verifique las propiedades de conexión, compruebe que hay una instancia de SQL Server ejecutándose en el host y aceptando las conexiones TCP/IP en el puerto y compruebe que no hay ningún firewall bloqueando las conexiones TCP en el puerto.".
 at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:191)
 at com.microsoft.sqlserver.jdbc.SQLServerException.ConvertConnectExceptionToSQLServerException(SQLServerException.java:242)
 at com.microsoft.sqlserver.jdbc.SocketFinder.findSocket(IOBuffer.java:2369)
 at com.microsoft.sqlserver.jdbc.TDSChannel.open(IOBuffer.java:551)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:1963)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:1628)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1459)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:773)
 at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:1168)
 at java.sql.DriverManager.getConnection(DriverManager.java:664)
 at java.sql.DriverManager.getConnection(DriverManager.java:247)
 at th.control.db.DBConnection.connect(DBConnection.java:30)

Acomodando las cosas para que SQLServer escuche

No tiene sentido que si usas el Microsoft SQL Server Management Studio, y ahí puedes hacer lo que te plazca (creates, updates, inserts, tanto de datos como esquemas, bases de datos, etc…) si tratas de conectarte desde otra aplicación, estén las puertas cerradas. Para abrirlas hay que hacer lo siguiente:

En una línea de comandos (cmd), o desde el comando ejecutar (Tecla Win + R), ejecuta los siguiente:

 C:\Windows\SysWOW64\SQLServerManager13.msc

Se te abrirá una ventana como la siguiente (yo ni sabía que existía). Es el SQL Server Configuration Manager.

001

Y tal como muestra la imagen siguiente, habilita las comunicación TCP/IP dentro de Protocolos de SQLEXPRESS.

003

El SQL Server Configuration Manager te va a decir que necesitas reiniciar el servicio para que los cambios tomen efecto, la siguiente imagen te indica cómo hacerlo.

004

Le damos play a la clase y…. Otra excepción… Lo bueno es que esta nos dice explícitamente que hacer!

run:
Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: No se pudo realizar la conexión con el host localhost, instancia con nombre sqlexpress. Error: "java.net.SocketTimeoutException: Receive timed out". Verifique los nombres del servidor y de instancia, compruebe que no hay ningún firewall bloqueando el tráfico UDP al puerto 1434. Para SQL Server 2005 o posterior, verifique que el servicio SQL Server Browser se está ejecutando en el host.
 at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:191)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.getInstancePort(SQLServerConnection.java:5247)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.primaryPermissionCheck(SQLServerConnection.java:1871)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:1608)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1459)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:773)
 at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:1168)
 at java.sql.DriverManager.getConnection(DriverManager.java:664)
 at java.sql.DriverManager.getConnection(DriverManager.java:247)
 at th.control.db.DBConnection.connect(DBConnection.java:30)

Nos dice que verifiquemos que un fulano servicio llamado SQL Server Browser debe estar iniciado. Y si revisamos la última imagen, hay algo ahí llamado igual. No hace falta imagen para esto sino sentido común: Clic derecho -> Iniciar… Play de nuevo!

run:
feb 05, 2017 9:59:38 AM com.microsoft.sqlserver.jdbc.AuthenticationJNI <clinit>
ADVERTENCIA: Failed to load the sqljdbc_auth.dll cause : no sqljdbc_auth in java.library.path
Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: Este controlador no está configurado para la autenticación integrada. ClientConnectionId:056b6f27-b750-4a8d-bda7-188d11f2aae9
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:2400)
 at com.microsoft.sqlserver.jdbc.AuthenticationJNI.<init>(AuthenticationJNI.java:68)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.logon(SQLServerConnection.java:3132)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.access$100(SQLServerConnection.java:43)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection$LogonCommand.doExecute(SQLServerConnection.java:3123)
 at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7505)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2445)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectHelper(SQLServerConnection.java:1981)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.login(SQLServerConnection.java:1628)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectInternal(SQLServerConnection.java:1459)
 at com.microsoft.sqlserver.jdbc.SQLServerConnection.connect(SQLServerConnection.java:773)
 at com.microsoft.sqlserver.jdbc.SQLServerDriver.connect(SQLServerDriver.java:1168)
 at java.sql.DriverManager.getConnection(DriverManager.java:664)
 at java.sql.DriverManager.getConnection(DriverManager.java:247)
 at th.control.db.DBConnection.connect(DBConnection.java:30)

Ok, esto sigue descontrolado! Y ahora qué?

Bueno, resulta que Microsoft permite dos maneras de conectarnos con su base de datos, y eso se lo decimos en el archivo de configuración. El parámetro integratedSecurity (integratedSecurity=true) indica si se usan las credenciales de Windows para conectarnos o si por el contrario, debemos especificar un usuario o contraseña (integratedSecurity=false), tal cual estamos haciendo. En otras palabras, SQLServer se esta pasando por el forro nuestro usuario y clave porque está usando nuestras credenciales de Windows para iniciar sesión…

Hay dos soluciones para esto… La que puedes inferir en el párrafo anterior (coloca en false el valor de integratedSecurity), o la otra, localizar el bendito sqljdbc_auth.dll y pegarlo en la carpeta System32 de Windows. Ambas funcionan, y el fulano .dll está en el mismo archivo donde bajaste los drivers JDBC de SQL Server hace unos parrafos mas arriba.

Cuando ejecutes de nuevo tu clase, este será el mensaje que obtendrás:

run:
Me conecté!!!

Saludos!