Hay muchas formas en que podemos conectar Java con Mysql y en este tutorial exploraremos algunas opciones para ver cómo lograrlo. Comenzaremos analizando posiblemente las opciones más populares que utilizan JDBC e Hibernate. Luego, también veremos algunas bibliotecas externas, incluidas MyBatis, Apache Cayenne y Spring Data. A lo largo del camino, proporcionaremos una serie de ejemplos prácticos.
Condiciones
Antes de continuar daremos por hecho que tenemos el servidor de Mysql instalado, pero si no es así te recomendamos revisar nuestro tutorial de cómo instalar Mysql en Ubuntu https://tenocode.com/instalar-mysql-en-ubuntu-22/
Crearemos una tabla para efectos de nuestro demo
CREATE TABLE person
(
ID INT,
FIRST_NAME VARCHAR(100),
LAST_NAME VARCHAR(100)
);
También necesitaremos el conector de java o driver mysql-connector-java, esta pieza hace el enlace entre nuestro servidor de Mysql y nuestra aplicación. Usaremos Maven para este ejemplo. OJO que las versiones de las librerías pueden variar en el tiempo.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
Conectando con JDBC
Para la mayoría de las conexiónes a base de datos, se necesitan algunas propiedades que listaremos a continuación.
Connection URL: Generalmente cuando conectamos con JDBC, necesitaremos una url de conexión, esta url no es más que un string, en el cual proporcionamos cierta información de nuestro servidor Mysql para poder acceder a él
jdbc:mysql://[host][,failoverhost...]
[:port]/[database]
[?propertyName1][=propertyValue1]
[&propertyName2][=propertyValue2]...
Un ejemplo real sería algo como:
jdbc:mysql://localhost:3306/test?serverTimezone=UTC
Driver class: el nombre de la clase que utilizaremos para la conexión. En nuestro caso es el controlador MySQL: com.mysql.cj.jdbc.Driver
Username and password: Credenciales necesarias para acceder al servidor Mysql seguramente.
Ejemplo de conexión JDBC
Código para conectarse y hacer una consulta a la tabla person
String sqlSelectAllPersons = "SELECT * FROM person";
String connectionUrl = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC";
try (Connection conn = DriverManager.getConnection(connectionUrl, "username", "password");
PreparedStatement ps = conn.prepareStatement(sqlSelectAllPersons);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
long id = rs.getLong("ID");
String name = rs.getString("FIRST_NAME");
String lastName = rs.getString("LAST_NAME");
// do something with the extracted data...
}
} catch (SQLException e) {
// handle the exception
}
Como podemos ver, dentro del try hacemos un loop para extraer la data de la consulta (ID, FIRST_NAME, LAST_NAME).
Conectando con ORMs (es lo que recomiendo)
En esta sección usaremos Hibernate, para manejar las conexiones y consulta a nuestra base de datos Mysql. Necesitamos agregar a nuestro pom las dependencias hibernate-core
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.4.2.Final</version>
</dependency>
Hibernate requiere que creemos una entidad por tabla, que son las entidades? aquí te lo muestro.
@Entity
@Table(name = "Person")
public class Person {
@Id
Long id;
@Column(name = "FIRST_NAME")
String firstName;
@Column(name = "LAST_NAME")
String lastName;
// getters & setters
}
El código anterior es una nueva clase con la anotación Entity. Esta clase es una representación en java de la tabla Person
Otro aspecto esencial es crear el archivo de recursos de Hibernate, normalmente llamado hibernate.cfg.xml, donde definiremos la información de configuración:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test?serverTimezone=UTC</property>
<property name="connection.username">username</property>
<property name="connection.password">password</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- Validate the database schema on startup -->
<property name="hbm2ddl.auto">validate</property>
<!-- Names the annotated entity class -->
<mapping class="Person"/>
</session-factory>
</hibernate-configuration>
Esta es solo una forma de configuración. Hibernate nos ofrece varias alternativas de configurar nuestra conexión a la base de datos, si necesitas ahondar más aún te dejo el link https://docs.jboss.org/hibernate/orm/6.4/quickstart/html_single/
El framework utiliza esta propiedad para convertir correctamente las declaraciones del lenguaje de consulta Hibernate (HQL) al SQL apropiado para nuestra base de datos determinada. Hibernate viene con más de 40 dialectos SQL. Como nos centramos en MySQL en este artículo, nos quedaremos con el dialecto MySQLDialect. Puedes revisar acá otros dialectos https://docs.jboss.org/hibernate/orm/6.4/dialect/dialect.html
Finalmente, Hibernate también necesita conocer el nombre completo de la clase a través de la etiqueta de mapeo. Una vez que completemos la configuración, usaremos la clase SessionFactory, que es la clase responsable de crear y agrupar conexiones JDBC.
Normalmente, esto sólo necesita configurarse una vez para una aplicación:
SessionFactory sessionFactory;
// configures settings from hibernate.cfg.xml
StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure().build();
try {
sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
} catch (Exception e) {
// handle the exception
}
Ahora que tenemos todo configurados podemos correr querys a nuestras tablas, ejemplo si quisieramos traer los datos de la tabla persona
Session session = sessionFactory.openSession();
session.beginTransaction();
List<Person> result = session.createQuery("from Person", Person.class).list();
result.forEach(person -> {
//do something with Person instance...
});
session.getTransaction().commit();
session.close();
Como se comentó anteriormente, también es posible crear la configuración de la conexión a la base de datos con un archivo java en vez de un xml. Para ello tambien utilizaremos un archivo de propiedades properties.
Archivo de propiedades, generalmente debe ir en la carpeta resources, lo llamaremos application.properties (puede tener cualquier nombre).
# Database connection settings
hibernate.connection.url=jdbc:mysql://localhost:3306/nombreDB
hibernate.connection.username=usuario
hibernate.connection.password=password
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
# Echo all executed SQL to console
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.highlight_sql=true
Aparte debes tener una clase que lea estas propiedades, tal como:
import lombok.Data;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
@Data
public class Props {
//DB Hibernate
private String driverDB;
private String urlConection;
private String userDB;
private String passDB;
public Props(){
try {
String rootPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
String appConfigPath = rootPath + "application.properties";
//System.out.println(rootPath);
Properties properties= new Properties();
properties.load(new FileInputStream(new File(appConfigPath)));
driverDB = (String) properties.get("hibernate.connection.driver_class");
urlConection = (String) properties.get("hibernate.connection.url");
userDB = (String) properties.get("hibernate.connection.username");
passDB = (String) properties.get("hibernate.connection.password");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
El archivo Java de conexión lo llamaremos HibernateUtil.java
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.service.ServiceRegistry;
import java.util.Properties;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static Props prop;
public static SessionFactory getSessionFactory() {
if (sessionFactory == null) {
try {
Configuration configuration = new Configuration();
prop = new Props();
// Hibernate settings equivalent to hibernate.cfg.xml's properties
Properties settings = new Properties();
settings.put(Environment.DRIVER, prop.getDriverDB());
settings.put(Environment.URL, prop.getUrlConection());
settings.put(Environment.USER, prop.getUserDB());
settings.put(Environment.PASS, prop.getPassDB());
settings.put(Environment.SHOW_SQL, "true");
settings.put(Environment.FORMAT_SQL, "true");
settings.put(Environment.HIGHLIGHT_SQL, "true");
settings.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
configuration.setProperties(settings);
configuration.addAnnotatedClass(Person.class);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception e) {
e.printStackTrace();
}
}
return sessionFactory;
}
}
Tambien puedes crear una query nativa:
public List<Object[]> listPerson() throws Exception{
List<Object[]> result = null;
String query="select * from person;";
Query queryResult = session.createNativeQuery(query);
result = queryResult.getResultList();
for(Object [] obj : result){
System.out.println(obj[0]); //id
System.out.println(obj[1]); //firstName
System.out.println(obj[2]); //lastName
}
return result;
}
MyBatis
Antes que todo dejo el link github del proyecto: https://github.com/mybatis/mybatis-3
MyBatis se introdujo en 2010 y es un framework de mapeo SQL con la simplicidad como su punto fuerte. Para usarlo, necesitamos agregar la dependencia mybatis:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
Suponiendo que reutilizamos la clase Persona anterior sin anotaciones, podemos proceder a crear una interfaz PersonMapper:
public interface PersonMapper {
String selectAll = "SELECT * FROM Person";
@Select(selectAll)
@Results(value = {
@Result(property = "id", column = "ID"),
@Result(property = "firstName", column = "FIRST_NAME"),
@Result(property = "lastName", column = "LAST_NAME")
})
List<Person> selectAll();
}
El siguiente paso tiene que ver con la configuración de MyBatis:
Configuration initMybatis() throws SQLException {
DataSource dataSource = getDataSource();
TransactionFactory trxFactory = new JdbcTransactionFactory();
Environment env = new Environment("dev", trxFactory, dataSource);
Configuration config = new Configuration(env);
TypeAliasRegistry aliases = config.getTypeAliasRegistry();
aliases.registerAlias("person", Person.class);
config.addMapper(PersonMapper.class);
return config;
}
DataSource getDataSource() throws SQLException {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setDatabaseName("test");
dataSource.setServerName("localhost");
dataSource.setPort(3306);
dataSource.setUser("username");
dataSource.setPassword("password");
dataSource.setServerTimezone("UTC");
return dataSource;
}
Creamos un objeto de Configuración que es un contenedor de configuraciones.
Luego podemos usar el objeto Configuración, que normalmente se configura una vez para que una aplicación cree una SqlSessionFactory:
Configuration configuration = initMybatis();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
try (SqlSession session = sqlSessionFactory.openSession()) {
PersonMapper mapper = session.getMapper(PersonMapper.class);
List<Person> persons = mapper.selectAll();
// do something with persons list ...
}
Apache Cayenne
Apache Cayenne es un marco de persistencia cuyo primer lanzamiento se remonta a 2002. Para obtener más información al respecto, sugerimos leer nuestra introducción a Apache Cayenne.
Como de costumbre, agreguemos la dependencia Maven del servidor cayenne:
<dependency>
<groupId>org.apache.cayenne</groupId>
<artifactId>cayenne-server</artifactId>
<version>4.0.2</version>
</dependency>
Nos centraremos específicamente en la configuración de conexión MySQL. En este caso, configuraremos cayenne-project.xml:
<?xml version="1.0" encoding="utf-8"?>
<domain project-version="9">
<map name="datamap"/>
<node name="datanode"
factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory"
schema-update-strategy="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy">
<map-ref name="datamap"/>
<data-source>
<driver value="com.mysql.cj.jdbc.Driver"/>
<url value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/>
<connectionPool min="1" max="1"/>
<login userName="username" password="password"/>
</data-source>
</node>
</domain>
Después de la generación automática de datamap.map.xml y la clase Person en forma de CayenneDataObject, podemos ejecutar algunas consultas.
Por ejemplo, continuaremos como antes con seleccionar todo:
ServerRuntime cayenneRuntime = ServerRuntime.builder()
.addConfig("cayenne-project.xml")
.build();
ObjectContext context = cayenneRuntime.newContext();
List<Person> persons = ObjectSelect.query(Person.class).select(context);
// do something with persons list...
Connecting Using Spring Data
Spring Data es un modelo de programación basado en Spring para el acceso a datos. Técnicamente, Spring Data es un proyecto general que contiene muchos subproyectos específicos de una base de datos determinada.
Veamos cómo usar dos de estos proyectos para conectarnos a una base de datos MySQL.
Spring Data / JPA
Spring Data JPA es un marco robusto que ayuda a reducir el código repetitivo y proporciona un mecanismo para implementar operaciones CRUD básicas a través de una de varias interfaces de repositorio predefinidas. Además de esto, tiene muchas otras funciones útiles.
El artefacto spring-data-jpa se puede encontrar en Maven Central:
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>3.2.2</version>
</dependency>
Continuaremos usando la clase Persona. El siguiente paso es configurar JPA usando anotaciones:
@Configuration
@EnableJpaRepositories("packages.to.scan")
public class JpaConfiguration {
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
dataSource.setUsername( "username" );
dataSource.setPassword( "password" );
return dataSource;
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabase(Database.MYSQL);
jpaVendorAdapter.setGenerateDdl(true);
return jpaVendorAdapter;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lemfb = new LocalContainerEntityManagerFactoryBean();
lemfb.setDataSource(dataSource());
lemfb.setJpaVendorAdapter(jpaVendorAdapter());
lemfb.setPackagesToScan("packages.containing.entity.classes");
return lemfb;
}
}
Para permitir que Spring Data implemente las operaciones CRUD, tenemos que crear una interfaz que extienda la interfaz CrudRepository:
@Repository
public interface PersonRepository extends CrudRepository<Person, Long> {
}
Y finalmente, veamos un ejemplo de seleccionar todo con Spring Data:
personRepository.findAll().forEach(person -> {
// do something with the extracted person
});
Spring Data / JDBC
Spring Data JDBC es una implementación limitada de la familia Spring Data, cuyo objetivo principal es permitir un acceso sencillo a bases de datos relacionales.
Por esta razón, no proporciona funciones como almacenamiento en caché, dirty tracking, lazy loading y muchas otras funciones JPA.
Esta vez la dependencia de Maven que necesitamos es spring-data-jdbc:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jdbc</artifactId>
<version>1.1.4.RELEASE</version>
</dependency>
La configuración es más ligera en comparación con la que usamos en la sección anterior para Spring Data JPA:
@Configuration
@EnableJdbcRepositories("packages.to.scan")
public class JdbcConfiguration extends AbstractJdbcConfiguration {
// NamedParameterJdbcOperations is used internally to submit SQL statements to the database
@Bean
NamedParameterJdbcOperations operations() {
return new NamedParameterJdbcTemplate(dataSource());
}
@Bean
PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
}
En el caso de Spring Data JDBC, tenemos que definir una nueva clase Person o modificar la existente para agregar algunas anotaciones específicas de Spring.
Esto se debe a que Spring Data JDBC se encargará directamente del mapeo de entidades en lugar de Hibernate:
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
@Table(value = "Person")
public class Person {
@Id
Long id;
@Column(value = "FIRST_NAME")
String firstName;
@Column(value = "LAST_NAME")
String lastName;
// getters and setters
}
Con Spring Data JDBC, también podemos usar la interfaz CrudRepository. Por lo tanto, la declaración será idéntica a la que escribimos anteriormente en el ejemplo de Spring Data JPA.