目录
1、JdbcTemplate
- Spring对JDBC进行封装,使用JdbcTemplate方便对数据库的操作。
- 引入依赖
- 在spring配置文件配置数据库连接池
- 配置jdbcTemplate对象,注入DataSource
- 创建service类,创建dao类,在dao注入jdbcTemplate对象
1.1、方法:
(1)增删改操作:
int update(String sql, Object... args);
(2)查询:返回某个值
T queryForObject(String sql,Class<T> requiredType);
(3)查询:返回某个对象
T queryForObject(String sql,RowMapper<T> rowMapper,Object ... args);
(4)查询:返回集合
List<T> query(String sql,RowMapper<T> rowMapper,Object... args);
(5)批量增删改:
int[] batchUpdate(String sql,List<Object[]> batchArgs);
1.2、举例:
- 引入相关jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
- 配置数据库连接池;配置JdbcTemplate对象
<context:component-scan base-package="com.oymn"></context:component-scan>
<!--配置数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/book" />
<property name="username" value="root" />
<property name="password" value="000000" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据库连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
- 创建Service类和Dao类,在Dao类中注入JdbcTemplate对象
public interface BookDao {
public void add(Book book); //添加图书
public void update(Book book); //修改图书
public void delete(int id); //删除图书
public int queryCount(); //查询数量
public Book queryBookById(int id); //查询某本书
public List<Book> queryBooks(); //查询所有书
public void batchAddBook(List<Object[]> books); //批量添加图书
public void batchUpdateBook(List<Object[]> books); //批量修改图书
public void batchDeleteBook(List<Object[]> args); //批量删除图书
}
@Repository
public class BookDaoImpl implements BookDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add(Book book) {
String sql = "insert into t_book set name=?,price=?";
Object[] args = {book.getBookName(),book.getBookPrice()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
@Override
public void update(Book book) {
String sql = "update t_book set name=?,price=? where id=?";
Object[] args = {book.getBookName(),book.getBookPrice(),book.getBookId()};
int update = jdbcTemplate.update(sql, args);
System.out.println(update);
}
@Override
public void delete(int id) {
String sql = "delete from t_book where id=?";
int update = jdbcTemplate.update(sql, id);
System.out.println(update);
}
@Override
public int queryCount() {
String sql = "select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
@Override
public Book queryBookById(int id) {
String sql = "select id bookId,name bookName,price bookPrice from t_book where id=?";
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
return book;
}
@Override
public List<Book> queryBooks() {
String sql = "select id bookId,name bookName,price bookPrice from t_book";
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
@Override
public void batchAddBook(List<Object[]> books) {
String sql = "insert into t_book set id=?,name=?,price=?";
int[] ints = jdbcTemplate.batchUpdate(sql, books);
System.out.println(ints);
}
@Override
public void batchUpdateBook(List<Object[]> books) {
String sql = "update t_book set name=?,price=? where id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, books);
System.out.println(ints);
}
@Override
public void batchDeleteBook(List<Object[]> args) {
String sql = "delete from t_book where id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, args);
System.out.println(ints);
}
}
@Service
public class BookService {
@Autowired
private BookDao bookDao = new BookDaoImpl();
//添加图书
public void add(Book book){
bookDao.add(book);
}
//修改图书
public void update(Book book){
bookDao.update(book);
}
//删除图书
public void delete(Integer id){
bookDao.delete(id);
}
//查询数量
public int queryCount(){
return bookDao.queryCount();
}
//查询图书
public Book queryBookById(Integer id){
return bookDao.queryBookById(id);
}
//查询所有图书
public List<Book> queryBooks(){
return bookDao.queryBooks();
}
//批量添加图书
public void batchAddBook(List<Object[]> books){
bookDao.batchAddBook(books);
}
//批量修改图书
public void batchUpdateBook(List<Object[]> books){
bookDao.batchUpdateBook(books);
}
//批量删除图书
public void batchDeleteBook(List<Object[]> args){
bookDao.batchDeleteBook(args);
}
}
2、事务管理
事务是数据库操作最基本单位,要么都成功,要么都失败。
- 典型场景:转账
- 事务四个特性ACID:
- 原子性
- 一致性
- 隔离性
- 持久性
- Spring事务管理有两种方式:
- 编程式事务管理
- 声明式事务管理
- 一般使用声明式事务管理,底层使用AOP原理。
- 声明式事务管理有两种方式:基于xml配置方式 和 基于注解方式,一般使用注解方式。
- Spring事务管理提供了一个接口,叫事务管理器,该接口针对不同的框架提供不同的实现类。
对于使用JdbcTemplate进行数据库交互,则使用DataSourceTransactionManager实现类,如果整合Hibernate框架则使用HibernateTransactionManager实现类,具体情况具体使用。
案例分析:
public interface UserDao {
//多钱
public void addMoney();
//少钱
public void reduceMoney();
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void addMoney() {
String sql = "update user_db set money = money + ? where username = ?";
jdbcTemplate.update(sql,100,"mary");
}
@Override
public void reduceMoney() {
String sql = "update user_db set money = money - ? where username = ?";
jdbcTemplate.update(sql,100,"lucy");
}
}
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账
public void accountMoney(){
//lucy少100
userDao.reduceMoney();
//mary多100
userDao.addMoney();
}
}
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="springtransaction"></context:component-scan>
<!--配置数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据库连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
public class TestBook {
@Test
public void testAccount(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}
}
上述代码,如果正常执行没有问题,但是如果代码执行过程中,出现异常会有问题。
问题例如:
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账
public void accountMoney(){
//lucy少100
userDao.reduceMoney();
//模拟异常
int i = 100/0;
//mary多100
userDao.addMoney();
}
}
如何解决:
使用事务,事务过程:
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账
public void accountMoney(){
try {
//1、开启事务
//2、处理逻辑
//lucy少100
userDao.reduceMoney();
//模拟异常
int i = 100/0;
//mary多100
userDao.addMoney();
//3、没有异常,提交事务
} catch (Exception e) {
//4、出现异常,事务回滚
}
}
}
1、事务添加到三层结果里Service层(业务逻辑层)
2、在Spring进行事务管理操作
- 有两种方式:编程式事务管理和声明式事务管理(使用)
3、编程式事务管理:上面代码的1234步骤
4、声明式事务管理有两种方式:基于xml配置方式 和 基于注解方式,一般使用注解方式。
5、在Spring进行声明式事务管理,底层使用AOP原理
6、Spring事务管理API
- 提供了一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类。
对于使用JdbcTemplate进行数据库交互,则使用DataSourceTransactionManager实现类,如果整合Hibernate框架则使用HibernateTransactionManager实现类,具体情况具体使用。
2.1、注解实现声明式事务管理:
- 在spring配置文件,配置事务管理器
- 在spring配置文件,开启事务注解
- 在service类上面或者service类的方法上面添加事务注解@Transactional
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/book" />
<property name="username" value="root" />
<property name="password" value="000000" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据库连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
在service类上面或者service类的方法上面添加事务注解@Transactional
- 如果把@Transactional添加在类上面,这个类里面所有方法都添加事务。
- 如果只是添加在方法上面,则只为这个方法添加事务。
声明式事务管理的参数配置:
在service中配置@Transactional注解,在这个注解里可以配置事务参数:
-
propagation:事务传播行为,总共有7种
事务方法:对数据库表数据进行变化的操作,比如更新,增加,删除。查询不是。
多事务方法直接进行调用,这个过程中事务是如何进行管理的。例如:
- 一个事务方法调用另一个无事务方法
- 一个无事务方法调用另一个事务方法
- 一个事务方法调用另一个事务方法
required:如果add方法本身有事务,调用update方法之后,update使用当前add方法里面事务,如果add方法本身没有事务,调用update方法之后,创建新事物
required_new:使用add方法调用update方法,如果add 无论是否有事务,都创建新的事务。
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class UserService {
}
-
isolation:事务隔离级别
不考虑隔离型会有三个读问题:脏读,不可重复读,虚读(幻读)。
设置隔离级别,解决读问题:
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {
}
3. timeout:超时时间
- 事务需要在一定时间内进行提交,超过时间后回滚。
- 默认值是-1,表示不超时,设置时间以秒为单位。
@Service
@Transactional(timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {
}
4. readOnly:是否只读
- 默认值为false,表示可以查询,也可以增删改。
- 设置为true,只能查询。
@Service
@Transactional(readOnly = true,timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {
}
5. rollbackFor:回滚,设置出现哪些异常进行事务回滚。
6. noRollbackFor:不回滚,设置出现哪些异常不进行事务回滚。
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
public class AccountService {
}
完全注解实现声明式事务管理:
创建配置类,使用配置类代替xml配置文件
@Configuration //配置类
@ComponentScan(basePackages = "com.oymn.spring5") //开启组件扫描
@EnableTransactionManagement //开启事务
public class Config {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
return druidDataSource;
}
//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//到ioc容器中根据类型找到dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
@Service
public class AccountService {
@Autowired
private AccountDao accountDao;
@Transactional
public void accountMoney(){
accountDao.add();
//int i=1/0; //用来模拟转账失败
accountDao.reduce();
}
}
(2)xml实现声明式事务管理:
- spring配置文件中配置事务管理器
- spring配置文件中配置通知
- spring配置文件中配置切入点和切面
<context:component-scan base-package="com.oymn"></context:component-scan>
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/book" />
<property name="username" value="root" />
<property name="password" value="000000" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据库连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<tx:method name="accountMoney" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!--配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.oymn.spring5.Service.*.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
3、Spring5新特性
- 整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除。
- Spring5.0框架 自带了通用的日志框架
3.1. 自带了日志封装
- Spring5移除了Log4jConfigListener,官方建议使用Log4j2
Spring5整合Log4j2:
第一步:引入jar包
<!--${log4j.version}这是版本信息,可以根据自己的需要填写,我用的是最新的2.11.2-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>${log4j.version}</version>
</dependency>
第二步:创建log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
public class UserLog {
private static final Logger log = LoggerFactory.getLogger(UserLog.class);
public static void main(String[] args) {
log.info("hello log4j2");
log.warn("hello log4j2");
}
}
02:33:32.740 [main] INFO springtransaction.UserLog - hello log4j2
02:33:32.747 [main] WARN springtransaction.UserLog - hello log4j2
3.2. @Nullable注解
-
@Nullable注解可以用在方法上,属性上,参数上,表示方法返回值可以为空,属性可以为空,参数可以为空。
@Nullable //表示方法返回值可以为空
public int getId();
@Nullable //表示参数可以为空
public void setId(@Nullable int Id);
@Nullable //表示属性可以为空
public int id;
3.3. 支持函数式风格编程
这是因为java8新增了lamda表达式
@Test
public void test() {
//1 创建 GenericApplicationContext 对象
GenericApplicationContext context = new GenericApplicationContext();
//2 调用 context 的方法对象注册
context.refresh();
context.registerBean("user1", User.class,() -> new User());
//3 获取在 spring 注册的对象
User user = (User)context.getBean("user1");
System.out.println(user);//aopanno.User@3d0f8e03
//3 获取在 spring 注册的对象
//使用全路径
context.registerBean( User.class,() -> new User());
User user1 = (User)context.getBean("com.atguigu.spring5.test.User");
System.out.println(user1);//aopanno.User@3d0f8e03
}
3.4. 支持整合JUnit5
(1)整合JUnit4:
第一步:引入jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
第二步:创建测试类,使用注解方式完成
@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架版本
@ContextConfiguration("classpath:bean4.xml") //加载配置文件
public class JUnitTest {
@Autowired
public User user;
@Test
public void test(){
System.out.println(user);
}
}
其中
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:bean1.xml") 相当于 ApplicationContext context=new AnnotationConfigApplicationContext(Config.class);
bean4.xml:
<context:component-scan base-package="com.oymn"></context:component-scan>
通过使用@ContextConfiguration注解,测试方法中就不用每次都通过context来获取对象了,比较方便。
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
BookService bookService = context.getBean("bookService",BookService.class);
(2)整合JUnit5:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>