学习内容:学习Spring框架(Day50)
1、Spring JDBC
2、Mysql事务
3、Mysql触发器
1、Spring JDBC
(1)使用Hikaricp连接池进行数据库连接,需要的jar包
mysql-connector-java.jar
HikariCP.jar
spring-jdbc.jar
(2)配置数据源文件db.properties,url需要设置时区,否则可能会报错
配置驱动Driver时,如果mysql-connector-java.jar版本在6以上,则是com.mysql.cj.jdbc.Driver,否则是com.mysql.jdbc.Driver
db.driver=com.mysql.cj.jdbc.Driver
db.userName=root
db.password=root
db.url=jdbc:mysql:///book_db?characterEncoding=utf8&serverTimezone=UTC
(3)在applicationContext.xml文件中,获取配置文件并配置数据源。使用spring-jdbc.jar提供的JdbcTemplate类进行对数据库的操作,将数据源注入JdbcTemplate类中,这个类就相当于之前学习的DBHelp类,里面提供了对数据库进行操作的方法。
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启基于注解的bean管理 依赖注入-->
<context:component-scan base-package="com.hisoft"/>
<!--获取配置文件-->
<context:property-placeholder location="db.properties"/>
<!--配置数据源-->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="username" value="${db.userName}"/>
<property name="password" value="${db.password}"/>
<property name="jdbcUrl" value="${db.url}"/>
<!--driver可以不用配置,导入mysql-connector-java.jar后会自动加载-->
<property name="driverClassName" value="${db.driver}"/>
</bean>
<!--构建spring JdbcTemplate-->
<bean id = "jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource"/>
</bean>
</beans>
(4)BookDaoImpl类通过注解放入spring容器,并将jdbcTemplate对象自动注入
@Repository //Dao层使用@Repository注解注册bean
public class BookDaoImpl implements BookDao {
@Autowired //自动注入jdbcTemplate对象
private JdbcTemplate jdbcTemplate;
@Override
public void save(Book book) {
String sql = "insert into book(bookname,author,publisher) values(?,?,?)";
jdbcTemplate.update(sql, book.getBookName(), book.getAuthor(), book.getPublisher());
}
public List<Book> findAll(){
String sql = "select id,bookname,author,publisher from book";
return jdbcTemplate.query(sql,new BookRowMapper());
}
@Override
public Long getCount() {
String sql = "select count(*) from book";
//使用局部匿名内部类
return jdbcTemplate.queryForObject(sql, new RowMapper<Long>() {
@Override
public Long mapRow(ResultSet rs, int i) throws SQLException {
return rs.getLong(1);
}
});
}
public Book findById(int id) {
String sql = "select id,bookname,author,publisher from book where id = ?";
//return jdbcTemplate.queryForObject(sql, new BookRowMapper(),id);
List<Book> bookList = jdbcTemplate.query(sql,new BookRowMapper(),id);
if(bookList.size() > 0){
return bookList.get(0);
}
return null;
}
//可以写一个内部类实现spring的RowMapper<>
private class BookRowMapper implements RowMapper<Book> {
@Override
public Book mapRow(ResultSet rs, int row) throws SQLException {
Book book = new Book();
book.setId(rs.getInt("id"));
book.setBookName(rs.getString("bookname"));
book.setAuthor(rs.getString("author"));
book.setPublisher(rs.getString("publisher"));
return book;
}
}
}
(5)BookServiceImpl类通过注解放入spring容器,并将BookDao对象自动注入
@Service("bookService")
public class BookServiceImpl {
@Autowired
private BookDao bookDao;
public void save(Book book){
bookDao.save(book);
}
public Book findById(int id){
return bookDao.findById(id);
}
public List<Book> findAll(){
return bookDao.findAll();
}
public Long getCount(){
return bookDao.getCount();
}
}
(6)测试
public class BookServiceTest {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookServiceImpl bookService = (BookServiceImpl) context.getBean("bookService");
System.out.println(bookService.getCount());
Book book = bookService.findById(101);
System.out.println(book.toString());
List<Book> all = bookService.findAll();
System.out.println(all.size());
Book book = new Book();
book.setBookName("红楼梦");
book.setPublisher("北京出版社");
book.setAuthor("曹雪芹");
bookService.save(book);
}
}
2、Mysql事务
(1)事务的四个特征(ACID属性)
原子性(Atomic):组成事务的处理语句组成了一个逻辑单元,这是最小的执行单位。
一致性(Consistent):在事务处理执行之前和之后,数据是一致的。
隔离性(Isolated):一个事务的处理对另一个事务没有影响。
持续性(Durable):当事务处理成功后,其结果在数据库中被永久记录下来。
(2)在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务,事务用来管理insert、update、delete语句,可以用来维护数据库的完整性,保证成批的SQL操作要么完全执行,要么完全不执行,事务处理的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时应归回滚,何时提交。
(3)事务的术语
事务(transaction):指一组SQL语句
回滚(rollback):指撤销指定SQL语句的过程
提交(commit):指将未存储的SQL语句结果写入到数据库中
保留点(savepoint):指事务处理中设置的临时占位符,可以对它进行回滚
(4)使用事务
开始事务:START TRANSACTION
回滚事务:ROLLBACK
/*事务演示*/
SELECT * FROM t_table;
START TRANSACTION;/*开始事务*/
DELETE FROM t_tableA WHERE id = 1;
ROLLBACK;/*事务回滚,删除没有成功*/
SELECT * FROM t_table;
提交事务:COMMIT
/*事务演示:提交*/
START TRANSACTION;
DELETE FROM t_tableA WHERE id = 1;
COMMIT; /*事务提交,删除成功*/
SELECT * FROM t_tableA;
使用保留点:
SAVEPOINT s1
ROLLBACK TO s1
START TRANSACTION;
DELETE FROM t_tableA WHERE id = 4;
SAVEPOINT s1; /*声明一个保留点*/
DELETE FROM t_tableA WHERE id = 5;
ROLLBACK TO s1; /*回滚到s1保留点*/
3、Mysql触发器
(1)触发器是MySQL响应insert、update、delete语句时自动执行的一条SQL语句,只有表支持触发器,视图不支持。
(2)触发器需要的信息
1.唯一的触发器名称(一个表中触发器名称唯一,而不是在一个数据库中唯一)
2.触发器关联的表
3.触发器应该响应的事件(insert?update?delete?)
4.触发器何时执行(处理之前或之后)
5.一个表的一个事件最多只能有两个触发器(处理之前和处理之后),所以一个表最多有6个触发器
6.如果响应之前的触发器执行失败,响应则不会执行;响应之前的触发器或响应执行失败,那么响应之后的触发器则不会执行
(3)inset触发器
/*触发器*/
CREATE TABLE t_tableA( /*两个测试表*/
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
val VARCHAR(20)
);
CREATE TABLE t_tableB(
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
val VARCHAR(20)
);
/*创建insert后置触发器*/
DELIMITER //
CREATE TRIGGER tr_insert_tableA /* tr_insert_tableA为触发器的名字*/
AFTER INSERT ON t_tableA /*一个后置的(AFTER)insert(INSERT )在t_tableA表上的触发器*/
FOR EACH ROW /*对所有代码行都执行*/
INSERT INTO t_tableB(val) VALUES(new.val);
END//
DELIMITER
/*触发器内容,将a表中插入的数据放到b表中一份,insert触发器会访问一个名称为new的虚拟表,获取刚插入的值*/
/*测试触发器*/
INSERT INTO t_tableA(val) VALUES('abc')
SELECT * FROM t_tableA;
SELECT * FROM t_tableB;
1.在Insert触发器内,可引用一个名为NEW的虚拟表,访问被插入的行
2.在before insert触发器中,NEW中的值也可以被更新(运行更改被插入的值)
3.对于自动增长列,NEW在insert执行之前的值为0,在执行之后是新的自动生成的值
/*第二个触发器:获取刚刚插入的自动生成的主键值*/
CREATE TRIGGER t_insert_pk_tableA
AFTER INSERT ON t_tableA
FOR EACH ROW SELECT new.id INTO @id;
/*测试触发器*/
INSERT INTO t_tableA(val) VALUES('abc');
SELECT @id;
(4)delete触发器
1.在DELETE触发器代码中,可以引用一个OLD的虚拟表,访问被删除的行
2.OLD表中的值全部是只读的,不能更新
DELIMITER //
CREATE TRIGGER t_delete_tableA
AFTER DELETE ON t_tableA /*DELETE后置触发器*/
FOR EACH ROW
BEGIN
INSERT INTO t_tableB (val) VALUES(old.val);
END//
DELIMITER;
/*测试触发器*/
DELETE FROM t_tableA WHERE id = 2
(5)update触发器
/*将a表中修改后的名字都改为大写*/
DELIMITER //
CREATE TRIGGER t_update_tableA
BEFORE UPDATE ON t_tableA
FOR EACH ROW
BEGIN
SET new.val = UPPER(new.val);
END//
DELIMITER;
/*测试触发器*/
UPDATE t_tableA SET val = 'xyz' WHERE id = 1;
SELECT * FROM t_tableA;
1.在UPDATE触发器代码中,可以引用一个名为OLD的虚拟表访问以前的值,引用一个名为NEW的表访问新更新的值
2.在befor update触发器中,new表中的值允许被更新(允许更改将要用于update语句中的值)
3.OLD表中的值都是只读的,不能更行
(6)删除一个触发器
DROP TRIGGER tr_insert_tableA