Distributed Transactions with Spring

https://bitbucket.org/catchers/atomikos-distributed-transactions/src/master/

AppConfig.java

package org.catcher.sandbox.learningspring.config;

import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class AppConfig {

	@Bean
	@Primary
	@ConfigurationProperties("sms.datasource")
	DataSource createPostgresSqlDataSource() {
		return new AtomikosDataSourceBean();
	}

	@Bean
	@ConfigurationProperties("complaint.datasource")
	DataSource createMySqlDataSource() {
		return new AtomikosDataSourceBean();
	}

	@Bean
	NamedParameterJdbcOperations smsJdbcTemplate() {
		return new NamedParameterJdbcTemplate(createPostgresSqlDataSource());
	}

	@Bean
	NamedParameterJdbcOperations complaintJdbcTemplate() {
		return new NamedParameterJdbcTemplate(createMySqlDataSource());
	}
}
package org.catcher.sandbox.learningspring.controller;

import org.catcher.sandbox.learningspring.service.ComplaintService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ComplaintController {

	private static final Logger logger = LoggerFactory.getLogger(ComplaintController.class);

	private final ComplaintService complaintService;

	public ComplaintController(ComplaintService complaintService) {
		this.complaintService = complaintService;
	}

	@RequestMapping("/complaint")
	public String createComplaint(@RequestParam String firstName, @RequestParam String lastName,
			@RequestParam String complaint, @RequestParam String phoneNumber) {
		logger.info("Complaint creation entered FirstName: {}, LastName: {}, Complaint: {}, PhoneNumber: {}", firstName,
				lastName, complaint, phoneNumber);
		complaintService.newComplaint(firstName, lastName, complaint, phoneNumber);
		return "OK";
	}
}
package org.catcher.sandbox.learningspring.repository;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.stereotype.Repository;

@Repository
public class ComplaintRepositoryImpl implements ComplaintRepository {

	private static final Logger logger = LoggerFactory.getLogger(ComplaintRepositoryImpl.class);

	private final NamedParameterJdbcOperations complaintJdbcTemplate;

	public ComplaintRepositoryImpl(NamedParameterJdbcOperations complaintJdbcTemplate) {
		this.complaintJdbcTemplate = complaintJdbcTemplate;
	}

	@Override
	public void storeData(String firstName, String lastName, String complaint) {
		logger.info("Storing Complaint Data, First Name: {}, Last Name: {}, Complaint {}", firstName, lastName,
				complaint);
		final String sql = "insert into complaint(firstname, lastname, complaint) values (:firstName, :lastName, :complaint)";
		MapSqlParameterSource paramSource = new MapSqlParameterSource("firstName", firstName);
		paramSource.addValue("lastName", lastName);
		paramSource.addValue("complaint", complaint);
		complaintJdbcTemplate.update(sql, paramSource);

		//throw new RuntimeException(); // Uncomment this line to verify distributed transactions
	}

}
package org.catcher.sandbox.learningspring.repository;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.stereotype.Repository;

@Repository
public class SmsRepositoryImpl implements SmsRepository {

	private static final Logger logger = LoggerFactory.getLogger(SmsRepositoryImpl.class);

	private final NamedParameterJdbcOperations smsJdbcTemplate;

	SmsRepositoryImpl(NamedParameterJdbcOperations smsJdbcTemplate) {
		this.smsJdbcTemplate = smsJdbcTemplate;
	}

	@Override
	public void storeSMS(String phoneNumber, String smsText) {
		logger.info("Storing SMS Data, Phone: {}, Text: {}", phoneNumber, smsText);
		final String sql = "insert into sms(sms_text, phone_number, insert_date) values (:smsText, :phoneNumber, :insertDate)";
		MapSqlParameterSource paramSource = new MapSqlParameterSource("smsText", smsText);
		paramSource.addValue("phoneNumber", phoneNumber);
		paramSource.addValue("insertDate", new Date());
		smsJdbcTemplate.update(sql, paramSource);
	}

}