предисловие
В предыдущей записи блога Spring в основном объяснялось, как использовать Spring для реализации АОП-программирования, а в этой записи блога в основном объяснялось, как использовать Spring для реализации АОП-программирования.Модуль Spring DAO поддерживает JDBC и управление транзакциями Spring....
Для JDBC мы, конечно, не новички, мыДолжно быть, я написал много кода шаблона JDBC, когда был новичком.!
Просмотрите процесс оптимизации кода шаблона
Давайте вспомним, как мы оптимизировали код нашего шаблона!
- Первый взгляд на насСобственный JDBC: вам нужно вручную перейти к драйверу базы данных, чтобы получить соответствующее соединение...
try {
String sql = "insert into t_dept(deptName) values('test');";
Connection con = null;
Statement stmt = null;
Class.forName("com.mysql.jdbc.Driver");
// 连接对象
con = DriverManager.getConnection("jdbc:mysql:///hib_demo", "root", "root");
// 执行命令对象
stmt = con.createStatement();
// 执行
stmt.execute(sql);
// 关闭
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
- Поскольку JDBC — это интерфейсно-ориентированное программирование, драйвер базы данных полностью создается производителем базы данных.Пока загружен соответствующий драйвер базы данных, можно получить соответствующее соединение с базой данных.....поэтому мыНаписал класс инструмента специально для подключения к базе данных (Connection), конечно, для большей гибкости нашКласс инструмента выполняется путем чтения файла конфигурации.
/*
* 连接数据库的driver,url,username,password通过配置文件来配置,可以增加灵活性
* 当我们需要切换数据库的时候,只需要在配置文件中改以上的信息即可
*
* */
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
//获取配置文件的读入流
InputStream inputStream = UtilsDemo.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(inputStream);
//获取配置文件的信息
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
//加载驱动类
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
public static void release(Connection connection, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- После вышеописанного слоя инкапсуляции мы можем напрямую использовать класс инструмента, чтобы получить подключение к базе данных в месте использования... Тогда это гораздо удобнее, чем оригинал! **но,Вам по-прежнему нужно использовать Connection для создания объекта Statement каждый раз. И неважно, какой метод, оператор SQL на самом деле отличается от переданных параметров!
- Итак, мы настроили класс инструмента JDBC, вы можете увидеть деталиblog.CSDN.net/hon_3 has/Aretti…
- Наш пользовательский класс инструментов на самом деле написан с использованием компонента DbUtils в качестве шаблона, поэтому мы разрабатывалиИспользование компонентов DbUtils.
Использование Spring JDBC
Выше мы рассмотрели нашу предыдущую разработку JDBC, поэтому давайте посмотрим, как Spring оптимизирует JDBC.
Прежде всего, если вы хотите использовать модуль Spring JDBC, вы должны ввести два файла jar:
-
импортировать JAR-файл
- spring-jdbc-3.2.5.RELEASE.jar
- spring-tx-3.2.5.RELEASE.jar
-
Сначала давайте взглянем на наш родной код JDBC:Получение Connection может быть извлечено, просто используйте dataSource напрямую, чтобы получить Connection.
public void save() {
try {
String sql = "insert into t_dept(deptName) values('test');";
Connection con = null;
Statement stmt = null;
Class.forName("com.mysql.jdbc.Driver");
// 连接对象
con = DriverManager.getConnection("jdbc:mysql:///hib_demo", "root", "root");
// 执行命令对象
stmt = con.createStatement();
// 执行
stmt.execute(sql);
// 关闭
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- Примечательно,JDBC имеет хорошую поддержку пула соединений с базой данных C3P0. Таким образом, мы можем напрямую использовать инъекцию зависимостей Spring, просто настройте источник данных в файле конфигурации.!
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
// IOC容器注入
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void save() {
try {
String sql = "insert into t_dept(deptName) values('test');";
Connection con = null;
Statement stmt = null;
// 连接对象
con = dataSource.getConnection();
// 执行命令对象
stmt = con.createStatement();
// 执行
stmt.execute(sql);
// 关闭
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
-
Spring предоставляет нам такой класс, как JdbcTemplate! Он инкапсулирует DataSource, что означает, что мы можем использовать JdbcTemplate в Dao.
-
Создайте источник данных, создайте объект jdbcTemplate
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///zhongfucheng"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!--扫描注解-->
<context:component-scan base-package="bb"/>
<!-- 2. 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
- userDao
package bb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
/**
* Created by ozc on 2017/5/10.
*/
@Component
public class UserDao implements IUser {
//使用Spring的自动装配
@Autowired
private JdbcTemplate template;
@Override
public void save() {
String sql = "insert into user(name,password) values('zhoggucheng','123')";
template.update(sql);
}
}
- контрольная работа:
@Test
public void test33() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bb/bean.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.save();
}
Запрос шаблона Jdbc
Если мы используем запрос JdbcTemplate, мы найдемСуществует много перегруженных методов query()
обычно,Если мы используем queryForMap(), мы можем инкапсулировать только одну строку данных, если мы инкапсулируем несколько строк данных, будет сообщено об ошибке.! Более того, Spring не знает, что мы хотим инкапсулировать в строку данных, поэтому возвращаемое значение — это коллекция Map. Если мы получаем коллекцию Map, нам нужно преобразовать ее в нужный нам тип.
Обычно мы используем следующий метод:
мы можемРеализовать RowMapper и рассказать Sppriing, как мы инкапсулируем каждую строку записей..
public void query(String id) {
String sql = "select * from USER where password=?";
List<User> query = template.query(sql, new RowMapper<User>() {
//将每行记录封装成User对象
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
return user;
}
},id);
System.out.println(query);
}
Конечно, в основном мыИнкапсулирует каждую строку записей в объект JavaBean, поэтому реализуйте RowMapper напрямую, просто создавайте его при использовании..
class MyResult implements RowMapper<Dept>{
// 如何封装一行记录
@Override
public Dept mapRow(ResultSet rs, int index) throws SQLException {
Dept dept = new Dept();
dept.setDeptId(rs.getInt("deptId"));
dept.setDeptName(rs.getString("deptName"));
return dept;
}
}
Обзор управления транзакциями
Далее в основном объясняется управление транзакциями Spring, как использовать Spring для управления транзакциями программы. …
- Управление транзакциями Spring принадлежит модулю Spring Dao..
Как правило, мыУправление транзакциями осуществляется на сервисном уровне.. . Почему это на уровне обслуживания, а не на уровне дао? ? У вас есть такие вопросы?..
Сервисный уровень — это уровень бизнес-логики.Успешное выполнение метода сервиса означает, что в функции нет ошибки..
ОдинМетод службы может вызывать несколько методов уровня dao.... Если вы выполняете управление транзакциями на уровне dao, метод dao работает неправильно, и он просто откатывает транзакцию к текущей функции dao, что неуместно [поскольку наш бизнес состоит из нескольких методов dao]. Если ошибки нет, транзакция фиксируется после вызова метода dao, что также неуместно [вызывает слишком много операций фиксации].
Существует два типа контроля транзакций:
- Программный контроль транзакций
- Декларативное управление транзакциями
Программный контроль транзакций
Самостоятельное управление транзакциями вручную называется программным управлением транзакциями.
- JDBC-код:
-
Conn.setAutoCommite(false); // 设置手动控制事务
-
- Спящий код:
-
Session.beginTransaction(); // 开启一个事务
-
- [Детальный контроль транзакций: вы можете добавить контроль транзакций к указанному методу и определенным строкам указанного метода]
- (Более гибкий, но более громоздкий в разработке: его нужно каждый раз открывать, отправлять и откатывать.)
Декларативное управление транзакциями
Управление контролем, предоставляемое Spring для транзакций, называется декларативным управлением транзакциями.
Spring предоставляет реализацию управления транзакциями.
- Если пользователь хочет использовать управление транзакциями Spring,Просто нужно настроить.
- Когда транзакция Spring не используется, просто удалите ее напрямую.
- Управление транзакциями Springна основе АОП. поэтому егоСцепление очень низкоеиз.
- [Грубый контроль транзакций:Транзакции могут применяться только ко всему методу, а не к определенным строкам метода.】
- (Поскольку aop перехватывает методы.)
Spring предоставляет нам класс менеджера транзакций, класс диспетчера транзакций делится на два типа, посколькуТранзакции JDBC отличаются от транзакций Hibernate.
- Класс декларативного менеджера транзакций Spring:
-
Jdbc技术:DataSourceTransactionManager
-
Hibernate技术:HibernateTransactionManager
-
Декларативное управление транзакциями
Давайте возьмем пример, основанный на Spring JDBC.
Импорт связанных пакетов jar
- JAR-пакеты, связанные с АОП [Поскольку декларативный контроль транзакций в Spring основан на АОП, необходимо представить jar-пакеты АОП. 】
- Ввести пространство имен tx
- Знакомство с пространством имен АОП
- Представьте пакет jdbcjar [пакет jdbc.jar и пакет tx.jar]
Создайте среду конфигурации
- написать интерфейс
public interface IUser {
void save();
}
- Класс реализации UserDao, используйте JdbcTemplate для работы с базой данных!
@Repository
public class UserDao implements IUser {
//使用Spring的自动装配
@Autowired
private JdbcTemplate template;
@Override
public void save() {
String sql = "insert into user(name,password) values('zhong','222')";
template.update(sql);
}
}
- userService
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void save() {
userDao.save();
}
}
- Конфигурация bean.xml: настроить пул соединений с базой данных, объект jdbcTemplate, аннотацию сканирования
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--数据连接池配置-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///zhongfucheng"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!--扫描注解-->
<context:component-scan base-package="bb"/>
<!-- 2. 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
При настройке среды ранее контроль транзакций отсутствует.
Это,Когда я дважды вызываю userDao.save() в службе, даже если в середине возникает исключение, я все равно могу вставить запись в базу данных.
- Код услуги:
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void save() {
userDao.save();
int i = 1 / 0;
userDao.save();
}
}
- Тестовый код:
public class Test2 {
@Test
public void test33() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bb/bean.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.save();
}
}
Реализация декларативного управления транзакциями в XML
Во-первых, мы должны настроить класс диспетчера транзакций: потому что управление транзакциями JDBC и Hibernate отличается.
<!--1.配置事务的管理器类:JDBC-->
<bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--引用数据库连接池-->
<property name="dataSource" ref="dataSource"/>
</bean>
Снова,Настройка того, как класс диспетчера транзакций управляет транзакциями
<!--2.配置如何管理事务-->
<tx:advice id="txAdvice" transaction-manager="txManage">
<!--配置事务的属性-->
<tx:attributes>
<!--所有的方法,并不是只读-->
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice>
Наконец,Настройте, какие методы перехватывать,
<!--3.配置拦截哪些方法+事务的属性-->
<aop:config>
<aop:pointcut id="pt" expression="execution(* bb.UserService.*(..) )"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
После завершения настройки методы службы должны управляться декларативной транзакцией Spring. Итак, давайте проверим это снова:
@Test
public void test33() {
ApplicationContext ac = new ClassPathXmlApplicationContext("bb/bean.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.save();
}
Реализовать управление транзакциями с помощью аннотированных методов.
Конечно, некоторым может показаться, что XML-файл требует слишком много настроек.Spring также предоставляет возможность использовать аннотации для управления транзакциями.
Первый шаг такой же, как и для XML,Класс диспетчера транзакций должен быть настроен:
<!--1.配置事务的管理器类:JDBC-->
<bean id="txManage" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--引用数据库连接池-->
<property name="dataSource" ref="dataSource"/>
</bean>
Шаг 2. Включите управление транзакциями с помощью аннотаций
<!--开启以注解的方式实现事务控制-->
<tx:annotation-driven transaction-manager="txManage"/>
Наконец, **Если вы хотите контролировать транзакцию метода, просто добавьте перед ней аннотацию @Transactional! ** Если вы хотите контролировать транзакцию всего класса, просто добавьте ее в класс.
@Transactional
public void save() {
userDao.save();
int i = 1 / 0;
userDao.save();
}
Свойства транзакции
На самом деле то, как мы управляем транзакциями в классе менеджера конфигурации XML, заключается в указании атрибутов транзакции! **Давайте посмотрим, какими свойствами обладает транзакция:
Что касается уровня изоляции транзакций, друзья, которые не понимают, могут обратиться к моему предыдущему сообщению в блоге:blog.CSDN.net/hon_3 has/Aretti…
Поведение распространения транзакции:
Глядя на приведенные выше атрибуты транзакции, я не коснулся того, что на самом деле это:propagation = Propagation.REQUIRED
Поведение распространения транзакции.
Существует так много атрибутов поведения распространения транзакций, но обычно используются только два:
- Propagation.REQUIRED [Если текущий метод уже имеет транзакцию,присоединиться к транзакции текущего метода】
- Propagation.REQUIRED_NEW [Если у текущего метода есть транзакция, транзакция текущего метода будет приостановлена.Всегда начинать новую транзакцию, транзакция текущего метода не запускается, пока не будет выполнена новая транзакция]
###Когда поведение распространения транзакции Propagation.REQUIRED###
- Теперь существует класс журнала, чье поведение распространения транзакций — Propagation.REQUIRED.
Class Log{
Propagation.REQUIRED
insertLog();
}
- Теперь я хочу войти перед сохранением
Propagation.REQUIRED
Void saveDept(){
insertLog();
saveDept();
}
Сам saveDept() имеет транзакцию.При вызове insertLog() транзакция insertLog() будет добавлена к транзакции saveDept()
Это,Метод saveDept() всегда является транзакцией, если в пути произойдет исключение, то данные вставкиLog() будут откатываться [потому что это внутри той же транзакции]
Void saveDept(){
insertLog(); // 加入当前事务
.. 异常, 会回滚
saveDept();
}
###Когда поведение распространения транзакции Propagation.REQUIRED_NEW###
- Теперь существует класс журнала, чье поведение распространения транзакций — Propagation.REQUIRED_NEW.
Class Log{
Propagation.REQUIRED
insertLog();
}
- Теперь я хочу войти перед сохранением
Propagation.REQUIRED
Void saveDept(){
insertLog();
saveDept();
}
При выполнении метода insertLog() в saveDept(),Метод insertLog() обнаруживает, что saveDept() уже имеет транзакцию, и insertLog() сама открывает новую транзакцию, пока транзакция не будет закрыта, а затем выполняет следующий метод.
Если в середине возникнет исключение, функция insertLog() не будет откатываться, потому что ее транзакция является собственной и была зафиксирована.
Void saveDept(){
insertLog(); // 始终开启事务
.. 异常, 日志不会回滚
saveDept();
}
Если в статье есть какие-либо ошибки, пожалуйста, поправьте меня, и мы сможем общаться друг с другом. Учащиеся, привыкшие читать технические статьи в WeChat и желающие получить больше ресурсов по Java, могутОбратите внимание на публичный аккаунт WeChat: Java3y