2.JDBC支持
2-1 使用JdbcTemplate
包含插入并返回主键的实现
将JdbcTemplate注入给对应的dao
将dataSource注入给JdbcTemplate
1.设置dataSources.properties文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/ums?useUnicode=true&characterEncoding=utf-8 jdbc.username=root jdbc.password=
2.配置spring.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" 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 https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 载入dataSources.properties classpath:最好写上,不写大的项目中可能出错 --> <context:property-placeholder location="classpath:dataSource.properties"/> <!-- 配置数据源的二种方式 --> <!-- 方式一 : 使用Spring内置的 --> <!--<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">--> <!--<property name="driverClassName" value="${jdbc.driver}"/>--> <!--<property name="url" value="${jdbc.url}"/>--> <!--<property name="username" value="${jdbc.username}"/>--> <!--<property name="password" value="${jdbc.password}"/>--> <!--</bean>--> <!-- 方式二 : 使用第三方的BasicDataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 最大连接数 --> <property name="maxActive" value="1"/> <!-- 初始化连接数 --> <property name="initialSize" value="1"/> <!-- 最长等待时间 如果在等待时间没有取到连接会出现报错 等待时间单位毫秒 --> <property name="maxWait" value="3000"/> </bean> <!-- 配置JDBCTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置UserDao 在UserDao的实现类UserDaoImpl中需要设置JdbcTemplate属性 所以需要在实现类中设设置属性属性 --> <bean id="userDao" class="dao.impl.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>
3.dao层的接口和实现类的设置
接口UserDao
import entity.User; import java.util.List; public interface UserDao { public void insertUser(User user); public void deleteUser(Integer id); public void updateUser(User user); public User selectById(Integer id); public List<User> selectAll(); public Long insertReturnPrimaryKey(User user); }
实现类UserDaoImpl
package dao.impl; import dao.UserDao; import entity.User; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; public class UserDaoImpl implements UserDao { //在实现类中JdbcTemplate设置属性 private JdbcTemplate jdbcTemplate; //实现set方法 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void insertUser(User user) { String sql = new StringBuffer() .append(" insert into ") .append(" t_user ") .append(" (username,password,phone,address)") .append(" values ") .append(" (?,?,?,?)") .toString(); jdbcTemplate.update(sql,user.getUsername(),user.getPassword(),user.getPhone(),user.getAddress()); } @Override public User selectById(Integer id) { String sql = new StringBuffer() .append(" select id,username,password,phone,address ") .append(" from t_user ") .append(" where id = ? ") .toString(); //关于结果集行处理可以实现匿名实现也可以单独写一个类处理2 List<User> users = jdbcTemplate.query(sql,new UserRowMapper(),id); return users.isEmpty()?null:users.get(0); } /** *jdbc中的返回主键,在做购物车业务时需要 **/ @Override public Long insertReturnPrimaryKey(User user) { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection con) throws SQLException { String sql = new StringBuffer() .append(" insert into ") .append(" t_user ") .append(" (username,password,phone,address)") .append(" values ") .append(" (?,?,?,?)") .toString(); PreparedStatement ps = con.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS); ps.setString(1,user.getUsername()); ps.setString(2,user.getPassword()); ps.setString(3,user.getPhone()); ps.setString(4,user.getAddress()); return ps; } },keyHolder); return (Long) keyHolder.getKey(); } }
4.测试
public class Test02 { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); // UserDao userDao = (UserDao) ac.getBean("userDao"); UserDao userDao = (UserDao) ac.getBean("userDao2"); User user = new User(); user.setUsername("tom"); user.setPassword("123456"); user.setPhone("13912345678"); user.setAddress("江苏-南京"); // userDao.insertUser(user); Long primaryKey = userDao.insertReturnPrimaryKey(user); System.out.println("返回的主键为:"+primaryKey); } }
2-2 使用JdbcDaoSupport
Spring定义一个JdbcDaoSupport类
该类是作为所有dao的父类而存在的
该类中注入了JdbcTemplate模板类
在具体dao中继承了JdbcDaoSupport之后
可以通过
getJdbcTemplate
的方法直接获取模板类由于dataSource是由开发者定义的
因此,我们可以直接在具体的dao中注入dataSource的值即可
<!--
载入dataSources.properties
classpath:最好写上,不写大的项目中可能出错
-->
<context:property-placeholder location="classpath:dataSource.properties"/>
<!-- 方式二 : 使用第三方的BasicDataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 最大连接数 -->
<property name="maxActive" value="1"/>
<!-- 初始化连接数 -->
<property name="initialSize" value="1"/>
<!--
最长等待时间
如果在等待时间没有取到连接会出现报错
等待时间单位毫秒
-->
<property name="maxWait" value="3000"/>
</bean>
<!-- 配置UserDao -->
<bean id="userDao" class="dao.impl.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!--
JdbcDaoSupport与配置JDBCTemplate不同
JdbcDaoSupport定义为所有dao的父类
所以直接注入就可以了
-->
<bean id="userDao2" class="dao.impl.UserDaoImpl2">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
//继承父类JdbcDaoSupport 直接注入getJdbcTemplate()方法.十分简单方便
public class UserDaoImpl2 extends JdbcDaoSupport implements UserDao {
@Override
public User selectById(Integer id) {
String sql = new StringBuffer()
.append(" select id,username,password,phone,address ")
.append(" from t_user ")
.append(" where id = ? ")
.toString();
List<User> users = getJdbcTemplate().query(sql,new UserRowMapper(),id);
return users.isEmpty()?null:users.get(0);
}
}
2.3jdbc行处理
import com.shoppingMVC.entity.Product;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
*这是一个产品的行处理
*需要实现结果集中的RowMapper接口
*返回对象
**/
public class ProductMapper implements RowMapper<Product> {
@Override
public Product mapperRow(ResultSet rs) throws SQLException {
Product p = new Product(
rs.getInt("id"),
rs.getString("name"),
rs.getDouble("price"),
rs.getString("image"),
rs.getString("info"),
rs.getInt("status")
);
return p;
}
}
3.MyBatis支持
3-1 POM配置
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
3-2 配置文件
1.UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.UserDao"> <sql id="userColumn"> id, username, password, phone, address </sql> <!-- Spring中通过扫包设置了别名 resultType中可以直接写别名就可以 如果没有设置需要写相对路径Entity.User --> <select id="selectAll" resultType="User"> select <include refid="userColumn"></include> from t_user </select> <insert id="insertUser" parameterType="User"> insert into t_user (username,password,phone,address) values (#{username},#{password},#{phone},#{address}) </insert> </mapper>
2.mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- sqlSessionFactory中整合了mybatis-config的配置 但是mybatis-config的插件任然可以被使用 --> <typeAliases></typeAliases> <mappers></mappers> </configuration>
3.spring.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" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://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/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 读取propert配置文件 --> <context:property-placeholder location="classpath:dataSource.properties"/> <!-- 设置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- sqlSessionFactory中整合了mybatis-config的配置 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource"/> <!-- 配置别名 扫包,在mapper中都可以直接使用类名--> <property name="typeAliasesPackage" value="entity"/> <!-- 注册mapper --> <property name="mapperLocations"> <list> <!--<value>classpath:mapper/UserMapper.xml</value>--> <!-- 支持通配符 --> <value>classpath:mapper/*.xml</value> </list> </property> <!-- 访问config配置 --> <!--<property name="configLocation" value="classpath:mybatis-config.xml"/>--> </bean> <!-- 配置UserDao --> <!-- 方式一:使用MapperFactoryBean生产对应的Mapper --> <!--<bean id="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">--> <!--<!– 注入SqlSessionFactory –>--> <!--<property name="sqlSessionFactory" ref="sqlSessionFactory"/>--> <!--<!– 配置需要生产的接口是谁 –>--> <!--<property name="mapperInterface" value="dao.UserDao"/>--> <!--</bean>--> <!-- 方式二:使用扫包的方式创建指定包下所有的Mapper 使用后处理bean对当前的工程做整体的操作 会自动扫描指定包下所有的接口 实现对这些接口的Mapper生产 生产出来的所有的Mapper其bean的id即为当前类名,首字母小写 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="dao"/> </bean> <!-- 事务配置 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源 --> <property name="dataSource" ref="dataSource"/> </bean> <!-- AOP2.X注解事务 --> <tx:annotation-driven transaction-manager="transactionManager"/> <context:component-scan base-package="service"/> </beans>
3-3 测试
package test;
import entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
/**
* Author:shixiaojun@itany.com
* Date:2020/7/3-9:29
*/
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring.xml");
// 方式一:使用MyBatis原始方式
// SqlSessionFactory factory = (SqlSessionFactory) ac.getBean("sqlSessionFactory");
// SqlSession session = factory.openSession();
// UserDao userDao = session.getMapper(UserDao.class);
// 方式二:使用MapperFactoryBean获取对应的Mapper
// UserDao userDao = (UserDao) ac.getBean("userDao");
// 方式三:使用扫包方式进行操作,bean的id为类名首字母小写
// UserDao userDao = (UserDao) ac.getBean("userDao");
// List<User> users = userDao.selectAll();
// for(User user : users){
// System.out.println(user);
// }
User user = new User();
user.setUsername("test4");
user.setPassword("666666");
user.setPhone("13945121238");
user.setAddress("江苏-南京");
//
// userDao.insertUser(user);
UserService userService = (UserService) ac.getBean("userServiceImpl");
System.out.println(userService);
userService.regist(user);
}
}
4.事务支持
4-1 什么是事务
所谓的事务,表示一次不可再分的操作序列
这些操作序列中的所有操作
要么都执行,要么都不执行
它是一个不可分割的完整的工作单元
4-2 传统数据库的事务特性
ACID
- A
- Atomicity
- 原子性
- C
- Consistency
- 一致性
- I
- Isolation
- 隔离性
- D
- Durability
- 持久性
4-3 Spring事务特性
- 传播行为
- 在Spring中,传播的规则有很多,一般需要熟练掌握其中两种
- REQUIRED
- 表示当前的方法必须运行在一个存在事务管理的环境中
- 在运行时会判断当前的环境中是否存在事务管理
- 如果存在,则会自动加入到当前的事务管理中
- 如果不存在,则会开启一个新的事务环境用于执行当前的业务
- 一般用于增删改操作
- SUPPORTS
- 表示当前的方法不是必须运行在事务管理的环境中
- 有事务可以运行,没有事务也可以运行
- 在运行时会判断当前的环境中是否存在事务管理
- 如果存在,则会自动加入到当前的事务管理中
- 如果不存在,则拉倒
- 一般用于查询操作
- 一般不会单独使用,会与其他属性联合使用
- 回滚条件
- 默认情况下,当遇到
RuntimeException
以及其子类的时候,会自动回滚 - 可以进行手动设置
rollbackFor="异常的class类型"
- 表示遇到指定的异常会进行回滚
noRollbackFor="异常的class类型"
- 表示遇到指定的异常不进行回滚
- 默认情况下,当遇到
- 只读优化
- 当你确定你的业务中有且仅有查询操作时才能使用
readOnly=true
- 一般与传播规则的SUPPORTS联合使用
- 超时
- Timeout
- 隔离级别
- 从小到大
TRANSACTION_NONE
- 指示事务不受支持的常量
- 即没有事务
TRANSACTION_READ_UNCOMMITTED
- 指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
- 即读未提交数据
TRANSACTION_READ_COMMITTED
- 指示不可以发生脏读的常量;不可重复读和虚读可以发生。
- 即读已提交数据
TRANSACTION_REPEATABLE_READ
- 指示不可以发生脏读和不可重复读的常量;虚读可以发生。
- 大部分的数据库均未对该级别做实现
- MySql与Oracle均未实现
TRANSACTION_SERIALIZABLE
- 指示不可以发生脏读、不可重复读和虚读的常量。
- 解决了并发
- 但是产生了悲观锁的问题
4-4 POM配置
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
4-5 Spring配置
以注解事务为例
Spring整合JDBC与MyBatis使用的是相同的事务管理器
DataSourceTransactionManager
<context:component-scan base-package="service"/>
<!-- 事务配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置AOP2.X注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
4-6 注解使用
@Transactional
- 表示配置事务的相关操作
- 该注解可以标注在类上,也可以标注在方法上
- 该注解标注在类上表示当前类均使用此时进行的事务配置
- 该注解标注在方法上表示当前方法使用此时进行的实物配置
- 如果类上与方法上均有该注解,优先使用方法上的事务配置
propagation
属性- 配置传播规则的事务属性
- 其值是一个枚举类型,使用
Propagation
中提供的值 - 其值一般使用
REQUIRED
或者SUPPOTRS
rollbackFor
属性- 配置回滚条件
- 当遇到指定的异常时进行回滚
noRollbackFor
属性- 配置回滚条件
- 当遇到指定的异常时不进行回滚
readOnly=true
- 配置是否只读
- 当操作中有且仅有查询操作时使用
@Service
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class,noRollbackFor = ArithmeticException.class)
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void regist(User user) throws UserExistException {
UserExample example = new UserExample();
example.or()
.andUsernameEqualTo(user.getUsername());
List<User> users = userMapper.selectByExample(example);
if(!users.isEmpty()){
throw new UserExistException("该用户已经被注册");
}
userMapper.insertSelective(user);
int i = 1/0;
}
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
@Override
public User login(String username, String password) throws UserNotExistException {
UserExample example = new UserExample();
example.or()
.andUsernameEqualTo(username)
.andPasswordEqualTo(password);
List<User> users = userMapper.selectByExample(example);
if(users.isEmpty()){
throw new UserNotExistException("用户名或密码错误");
}
return users.get(0);
}
}
5.web支持
5-1 POM配置
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- J2EE 环境依赖 begin -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- J2EE 环境依赖 end -->
5-2 tomcat插件
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 配置应用名,在Maven开发中,一般配置空就行 -->
<path>/</path>
<!-- 配置端口号 -->
<port>8080</port>
</configuration>
</plugin>
5-3 配置监听器
配置Application创建销毁事件的一个监听器
用于在服务器启动的时候解析Spring容器
该监听器由Spring提供,我们需要对其进行配置即可
Spring提供的监听器为:
ContenxtLoaderListener
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
但是,由于Spring的容器是由开发者定义的
Spring无法预先知道我们的配置文件放在哪
因此,提供了一个全局初始化参数,用于指定容器的位置
该参数的key为
contextConfigLocation
通过全局初始化对该参数值可以配置
<!-- 配置全局初始化参数,用于指定Spring配置文件的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 初始化参数的值支持通配符 -->
<!--<param-value>classpath:applicationContext.xml</param-value>-->
<param-value>classpath:applicationContext*.xml</param-value>
</context-param>
5-4 UserServlet
WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
UserService userService = (UserService) ac.getBean("userServiceImpl");
String username = request.getParameter("username");
String password = request.getParameter("password");
try {
User user = userService.login(username, password);
request.getSession().setAttribute("user", user);
response.sendRedirect(request.getContextPath() + "/regist.jsp");
} catch (UserNotExistException e) {
request.setAttribute("loginMsg", e.getMessage());
request.getRequestDispatcher("/login.jsp").forward(request, response);
}