Программный метод управления транзакциями позволяет нам управлять транзакциями с помощью программного кода. Как уже упоминалось, главный плюс этого подхода – невероятная гибкость, а минус – сложность в поддержании.
Для понимания, что такоe, программный метод управления транзакциями на практике рассмотрим пример небольшого приложения.
Пример приложения:
Исходный код проекта можно скачать по ЭТОЙ ССЫЛКЕ.
Создадим в нашей базе данных (далее – БД) две таблицы.
Таблица DEVELOPERS
CREATE TABLE DEVELOPERS(
ID INT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(50) NOT NULL,
SPECIALTY VARCHAR(50) NOT NULL,
EXPERIENCE INT NOT NULL,
PRIMARY KEY (ID)
);
Таблица PROJECTS
CREATE TABLE PROJECTS(
DEVELOPERS_ID INT NOT NULL,
NAME VARCHAR(50) NOT NULL,
COMPANY VARCHAR(50) NOT NULL
);
Первая таблица содержит данные о разработчиках, а вторая – о проектах, в которых участвовал разработчик.
Прим.: Для реализации программного метода мы будем использовать PlatformTransactionManager
Структура проекта
Developer
package net.proselyte.transactionsProgrammatic.model;
import java.util.List;
public class Developer {
private Integer id;
private String name;
private String specialty;
private List projects;
private Integer experience;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
public List getProjects() {
return projects;
}
public void setProjects(List projects) {
this.projects = projects;
}
public Integer getExperience() {
return experience;
}
public void setExperience(Integer experience) {
this.experience = experience;
}
@Override
public String toString() {
return "Developer:\n" +
"id: " + id +
"\nName: " + name +
"\nSpecialty: " + specialty +
"\nExperience: " + experience + "\n";
}
}
Project
package net.proselyte.transactionsProgrammatic.model;
public class Project {
private Integer developersId;
private String projectName;
private String companyName;
public Integer getDevelopersId() {
return developersId;
}
public void setDevelopersId(Integer developersId) {
this.developersId = developersId;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
@Override
public String toString() {
return "Project:" +
"\nDeveloper's Id: " + developersId +
"\nProject name: " + projectName +
"\nCompanyName: " + companyName + "\n";
}
}
DeveloperDao
package net.proselyte.transactionsProgrammatic.dao;
import net.proselyte.transactionsProgrammatic.model.Developer;
import net.proselyte.transactionsProgrammatic.model.Project;
import javax.sql.DataSource;
import java.util.List;
public interface DeveloperDao {
public void setDataSource(DataSource dataSource);
public void createDeveloper(String name, String specialty, Integer experience);
public List listDevelopers();
public List listDevelopersProjects(Integer id);
}
ProjectDao
package net.proselyte.transactionsProgrammatic.dao;
import javax.sql.DataSource;
public interface ProjectDao {
public void setDataSource(DataSource dataSource);
public void createProject(Integer developersId, String projectName, String companyName);
}
JdbcTemplateDeveloperDaoImpl
package net.proselyte.transactionsProgrammatic.dao.jdbcTemplate;
import net.proselyte.transactionsProgrammatic.dao.DeveloperDao;
import net.proselyte.transactionsProgrammatic.model.Developer;
import net.proselyte.transactionsProgrammatic.model.Project;
import net.proselyte.transactionsProgrammatic.utils.DevelopersMapper;
import net.proselyte.transactionsProgrammatic.utils.ProjectsMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import javax.sql.DataSource;
import java.util.List;
public class JdbcTemplateDeveloperDaoImpl implements DeveloperDao {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
private PlatformTransactionManager transactionManager;
@Override
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public void createDeveloper(String name, String specialty, Integer experience) {
TransactionDefinition definition =
new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
String SQL = "INSERT INTO DEVELOPERS (DEVELOPER_NAME, SPECIALTY, EXPERIENCE) VALUES (?,?,?)";
jdbcTemplate.update(SQL, name, specialty, experience);
System.out.println("Developer's record created/updated successfully. Name: " +
name + ", Specilaty: " + specialty + ", Experience: " + experience + "\n");
transactionManager.commit(status);
}
@Override
public List listDevelopers() {
String SQL = "SELECT * FROM DEVELOPERS";
List developers = jdbcTemplate.query(SQL, new DevelopersMapper());
return developers;
}
@Override
public List listDevelopersProjects(Integer id) {
String SQL = "SELECT * FROM PROSELYTE_TUTORIAL.PROJECTS WHERE DEVELOPERS_ID = " + id;
List projectList = jdbcTemplate.query(SQL, new ProjectsMapper());
return projectList;
}
}
JdbcTemplateProjectDaoImpl
package net.proselyte.transactionsProgrammatic.dao.jdbcTemplate;
import net.proselyte.transactionsProgrammatic.dao.ProjectDao;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import javax.sql.DataSource;
public class JdbcTemplateProjectDaoImpl implements ProjectDao {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
private PlatformTransactionManager transactionManager;
@Override
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public void createProject(Integer developerId, String projectName, String companyName) {
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
String SQL = "INSERT INTO PROJECTS (DEVELOPERS_ID, NAME, COMPANY) VALUES (?,?,?)";
jdbcTemplate.update(SQL, developerId, projectName, companyName);
System.out.println("Project record created successfully. Project name: " +
projectName + ", Company: " + companyName + "\n");
transactionManager.commit(status);
}
}
DeveloperMapper
package net.proselyte.transactionsProgrammatic.utils;
import net.proselyte.transactionsProgrammatic.model.Developer;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DevelopersMapper implements RowMapper {
@Override
public Developer mapRow(ResultSet rs, int rowNum) throws SQLException {
Developer developer = new Developer();
developer.setId(rs.getInt("id"));
developer.setName(rs.getString("developer_name"));
developer.setSpecialty(rs.getString("specialty"));
developer.setExperience(rs.getInt("experience"));
return developer;
}
}
ProjectMapper
package net.proselyte.transactionsProgrammatic.utils;
import net.proselyte.transactionsProgrammatic.model.Project;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
public class ProjectsMapper implements RowMapper {
@Override
public Project mapRow(ResultSet rs, int rowNum) throws SQLException {
Project project = new Project();
project.setDevelopersId(rs.getInt("DEVELOPERS_ID"));
project.setProjectName(rs.getString("NAME"));
project.setCompanyName(rs.getString("COMPANY"));
return project;
}
}
Конфигурационный файл transactions-programmatic-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
<!-- Initialization for data source -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ИМЯ_ВАШЕЙ_БАЗЫ_ДАННЫХ"/>
<property name="username" value="ИМЯ_ПОЛЬЗОВАТЕЛЯ"/>
<property name="password" value="ВАШ_ПАРОЛЬ"/>
</bean>
<!-- Initialization for TransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Definition for developerJDBCTemplate bean -->
<bean id="developerJDBCTemplate"
class="net.proselyte.transactionsProgrammatic.dao.jdbcTemplate.JdbcTemplateDeveloperDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!-- Definition for projectJDBCTemplate bean -->
<bean id="projectJDBCTemplate"
class="net.proselyte.transactionsProgrammatic.dao.jdbcTemplate.JdbcTemplateProjectDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="transactionManager"/>
</bean>
</beans>
ApplicationRunner
package net.proselyte.transactionsProgrammatic;
import net.proselyte.transactionsProgrammatic.dao.jdbcTemplate.JdbcTemplateDeveloperDaoImpl;
import net.proselyte.transactionsProgrammatic.dao.jdbcTemplate.JdbcTemplateProjectDaoImpl;
import net.proselyte.transactionsProgrammatic.model.Developer;
import net.proselyte.transactionsProgrammatic.model.Project;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class ApplicationRunner {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("transactions-programmatic-config.xml");
JdbcTemplateDeveloperDaoImpl developerDao =
(JdbcTemplateDeveloperDaoImpl) context.getBean("developerJDBCTemplate");
JdbcTemplateProjectDaoImpl projectDao =
(JdbcTemplateProjectDaoImpl) context.getBean("projectJDBCTemplate");
System.out.println("======== Creating project's records ========");
projectDao.createProject(82, "ProselyteTutorial", "Proselyte.net");
projectDao.createProject(82, "SkybleLib", "SkybleSoft");
System.out.println("======== Creating developer's records ========");
developerDao.createDeveloper("Proselyte", "Java Developer", 3);
developerDao.createDeveloper("Mike", "C++ Developer", 5);
System.out.println("======== List of Developers ========");
List developerList = developerDao.listDevelopers();
for (Developer developer : developerList) {
System.out.println(developer);
}
System.out.println("======== Proselyte Developer's Projects ========");
List projects = developerDao.listDevelopersProjects(82);
for (Project project : projects) {
System.out.println(project);
}
}
}
Результат работы программы
=========
/usr/lib/jvm/java-8-oracle/bin/java -Didea.launcher.port=7535 -Didea.launcher.bin.path=/home/proselyte/Programming/Soft/IntellijIdea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-8-oracle/jre/lib/management-agent.jar:/usr/lib/jvm/java-8-oracle/jre/lib/plugin.jar:/usr/lib/jvm/java-8-oracle/jre/lib/rt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jsse.jar:/usr/lib/jvm/java-8-oracle/jre/lib/charsets.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jce.jar:/usr/lib/jvm/java-8-oracle/jre/lib/resources.jar:/usr/lib/jvm/java-8-oracle/jre/lib/deploy.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfxswt.jar:/usr/lib/jvm/java-8-oracle/jre/lib/javaws.jar:/usr/lib/jvm/java-8-oracle/jre/lib/jfr.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-8-oracle/jre/lib/ext/jfxrt.jar:/home/proselyte/Programming/IdeaProjects/ProselyteTutorials/target/classes:/home/proselyte/.m2/repository/org/springframework/spring-core/4.1.1.RELEASE/spring-core-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/commons-logging/commons-logging/1.1.3/commons-logging-1.1.3.jar:/home/proselyte/.m2/repository/org/springframework/spring-web/4.1.1.RELEASE/spring-web-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-beans/4.1.1.RELEASE/spring-beans-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-context/4.1.1.RELEASE/spring-context-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-aop/4.1.1.RELEASE/spring-aop-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/home/proselyte/.m2/repository/org/springframework/spring-jdbc/4.1.1.RELEASE/spring-jdbc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-tx/4.1.1.RELEASE/spring-tx-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-webmvc/4.1.1.RELEASE/spring-webmvc-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/springframework/spring-expression/4.1.1.RELEASE/spring-expression-4.1.1.RELEASE.jar:/home/proselyte/.m2/repository/org/aspectj/aspectjtools/1.8.8/aspectjtools-1.8.8.jar:/home/proselyte/.m2/repository/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar:/home/proselyte/.m2/repository/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar:/home/proselyte/Programming/Soft/IntellijIdea/lib/idea_rt.jar com.intellij.rt.execution.application.AppMain net.proselyte.transactionsProgrammatic.ApplicationRunner
Feb 09, 2016 2:07:19 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@246b179d: startup date [Tue Feb 09 14:07:19 EET 2016]; root of context hierarchy
Feb 09, 2016 2:07:20 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [transactions-programmatic-config.xml]
Feb 09, 2016 2:07:20 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
======== Creating project's records ========
Tue Feb 09 14:07:20 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Project record created successfully. Project name: ProselyteTutorial, Company: Proselyte.net
Tue Feb 09 14:07:21 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Project record created successfully. Project name: SkybleLib, Company: SkybleSoft
Tue Feb 09 14:07:21 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
======== Creating developer's records ========
Developer's record created/updated successfully. Name: Proselyte, Specilaty: Java Developer, Experience: 3
Tue Feb 09 14:07:21 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Developer's record created/updated successfully. Name: Mike, Specilaty: C++ Developer, Experience: 5
======== List of Developers ========
Tue Feb 09 14:07:21 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Developer:
id: 82
Name: Proselyte
Specialty: Java Developer
Experience: 3
Developer:
id: 83
Name: Mike
Specialty: C++ Developer
Experience: 5
======== Proselyte Developer's Projects ========
Tue Feb 09 14:07:22 EET 2016 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Project:
Developer's Id: 82
Project name: 'ProselyteTutorial
CompanyName: Proselyte.net
Project:
Developer's Id: 82
Project name: 'SkybleLib
CompanyName: SkybleSoft
Примечания: если возникают проблемы с чтением из базы данных – обратите внимание на ID разработчиков.
В этом примере мы ознакомились с основами управления транзакциями программным методом.