2019尚硅谷经典Java面试题(第1季)02,Spring Bean作用域,事务传播行为和隔离级别,SpringMVC乱码,MyBatis属性名和表字段不一致

1.Spring Bean的作用域之间有什么区别

文字说明

bean的作用域
可以通过scope属性来指定bean的作用域

  • -singleton:默认值。当IOC容器——创建就会创建bean的实例,而且是单例的,每次得到的都是同一个

    • 在SpringlOC容器中仅存在一个Bean实例,Bean以单实例的方式存在
  • -prototype:原型的。当IOC容器——创建不再实例化该bean,而且每调用一次创建一个对象

    • 每次调用getBean方法时再实例化该bean,每次调用getBean()时都会返回一个新的实例
  • -request:每次请求实例化一个bean

    • 每次HTTP请求都会创建一个新的 Bean,该作用域仅适用于WebApplicationContext环境

    • 必须是 web环境

  • -session:在一次会话中共享一个bean

    • 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的 Bean。该作用域仅适用于WebApplicationContext环境
  • globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

在Spring 中,可以在元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。

默认情况下, Sdring 只为每个在IOC容器里声明的bean 创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和 bean引用都将返回这个唯一的bean 实例。该作用域被称为singleton,它是所有bean的默认作用域。

项目搭建

book类
public class Book {

	private Integer id;
	private String title;
	private String author;
	private double price;
	private Integer sales;

	public Book() {
		System.out.println("Book对象被创建了");
	}

	public Book(Integer id, String title, String author, double price, Integer sales) {
		super();
		this.id = id;
		this.title = title;
		this.author = author;
		this.price = price;
		this.sales = sales;
		System.out.println("全参的构造器被调用");
	}

	public Book(Integer id, String title, String author, double price) {
		super();
		this.id = id;
		this.title = title;
		this.author = author;
		this.price = price;
		System.out.println("不含销量的构造器被调用");
	}

	public Book(Integer id, String title, String author, Integer sales) {
		super();
		this.id = id;
		this.title = title;
		this.author = author;
		this.sales = sales;
		System.out.println("不含价格的构造器被调用");
	}
	//get set toString
}
测试类
class SpringTest {

	//01.Spring Bean的作用域之间有什么区别

	//创建IOC容器对象
	ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
	
	@Test
	void testBook() {
		Book book = (Book) ioc.getBean("book");
		Book book2 = (Book) ioc.getBean("book");
		System.out.println(book==book2);
	}
}
xml类
  • beans.xml,在 config目录下,编译后:变成 bin下
    • config 和 src 都是源文件夹,相当于在 src 下
    • bin 是排除的
<?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 http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<bean id="book" class="com.atguigu.spring.beans.Book" scope="prototype">
	 	<property name="id" value="8"></property>
	 	<property name="title" value="红高粱"></property>
	 	<property name="author" value="莫言"></property>
	 	<property name="price" value="10.00"></property>
	 	<property name="sales" value="800"></property>
	</bean>
	
</beans>
要引入的包
commons-logging-1.1.3.jar
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar

2. 事务传播属性和隔离级别

  1. 请简单介绍Spring支持的常用数据库事务传播属性和事务隔离级别?

传播行为

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为。

propagation
英
/ˌprɒpəˈɡeɪʃn/
n.
(动植物等的)繁殖,增殖,;(观点、理论等的)传播;(运动、光线、声音等的)传送

mandatory
英
/ˈmændətəri/
adj.
强制性的,义务的;受(前国际联盟)委任统治的
n.
受托人,代理人(=mandatary)

nested
英
/ˈnestɪd/
adj.
嵌套的,内装的
v.
筑巢;嵌入(nest 的过去分词)
  • 2个不支持事务。
    • 一个报错。
    • 一个挂起。
  • 5个支持事务。
    • 有就用,没有就创建。
    • 支持你,你有 我就有。
    • 强制,你没有事务,就抛异常。
    • 我创建新事务,把你的挂起。
      • 我创建新事务,嵌套你的内部。
public enum Propagation {
    REQUIRED(0), //required 需要的。有就用,没有就新建。
    SUPPORTS(1), //supports 支持。支持你,你有,我就有。
    
    MANDATORY(2), //强制 运行在事务内部,没有 就抛出异常。
    REQUIRES_NEW(3),//require的第三人称单数形式。我创建新的事务,把你挂起。
    
    NOT_SUPPORTED(4),//不支持事务。你有事务,把你的挂起。
    NEVER(5),//从不,决不。有运行的事务、就抛出异常
    
    NESTED(6);//嵌套。我启动一个新的,你有就和你嵌条。
}

//REQUIRED
//如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行

//REQUIRES_NEW
//当前的方法必须启动新事务,并在它自己的事务内运行.如果有事务正在运行,应该将它挂起

//SUPPORTS
//如果有事务在运行,当前的方法就在这个事务内运行.否则它可以不运行在事务中.

//NOT_SUPPORTED
//当前的方法不应该运行在事务中.如果有运行的事务,将它挂起

//MANDATORY
//当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常

//NEVER
//当前的方法不应该运行在事务中.如果有运行的事务、就抛出异常

//NESTED
//如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行.否则,就启动一个新的事务,并在它自己的事务内运行.

//事务传播属性可以在@Transactional 注解的propagation属性中定义。

代码演示

去结账 接口和实现
  • 去结账 开始了事务,调用
public interface Cashier {

	/**
	 * 去结账
	 * 
	 * @param userId
	 * @param isbns
	 */
	void checkout(int userId, List<String> isbns);
}


@Service("cashier")
public class CashierImpl implements Cashier {

	@Autowired
	private BookShopService bookShopService;
	
	@Transactional //去结账开始了事务
	@Override
	public void checkout(int userId, List<String> isbns) {
		for (String isbn : isbns) {
			//调用BookShopService中买东西的方法
			bookShopService.purchase(userId, isbn);
		}
	}

}
买东西的接口和实现
  • 被结账调用,结账开始了事务,
    • 我的买东西方法,也是开始事务的。
public interface BookShopService {

	/**
	 * 买东西
	 * 
	 * @param userId
	 * @param isbn
	 */
	void purchase(int userId, String isbn);
}
/**
 * @Transactional注解
 * 	该注解可以添加到类上,也可以添加到方法上
 * 	如果添加到类上,那么类中所有的方法都添加上了事务
 * 	如果添加到方法上,只有添加了该注解的方法才添加了事务
 */
//@Transactional
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {

	@Autowired
	private BookShopDao bookShopDao;

    @Transactional(propagation=Propagation.REQUIRED,
                   isolation=Isolation.READ_COMMITTED)
	@Override
	public void purchase(int userId, String isbn) {
		//1.获取要买的图书的价格
		double bookPrice = bookShopDao.getBookPriceByIsbn(isbn);
//		System.out.println(bookPrice);
		//2.更新图书的库存
		bookShopDao.updateBookStock(isbn);
		//3.更新用户的余额
		bookShopDao.updateAccountBalance(userId, bookPrice);
//		double bookPriceByIsbn = bookShopDao.getBookPriceByIsbn(isbn);
//		System.out.println(bookPriceByIsbn);
	}

}
结果
/**
 * 事务的属性:
 *     1.★propagation:用来设置事务的传播行为
 *        事务的传播行为:一个方法运行在了一个开启了事务的方法中时,当前方法是使用原来的事务还是开启一个新的事务
 		//A方法,运行在 B中(已开事务)。A使用B的事务?还是另开?
        //purchase方法 是使用,自己的事务,还是使用 调用者 结账checkout 的事务?
*/

 /*
 *        -Propagation.REQUIRED:默认值,使用原来的事务
 *        -Propagation.REQUIRES_NEW:将原来的事务挂起,开启一个新的事务
 
 *     2.★isolation:用来设置事务的隔离级别
 *        -Isolation.REPEATABLE_READ:可重复读,MySQL默认的隔离级别
 *        -Isolation.READ_COMMITTED:读已提交,Oracle默认的隔离级别,开发时通常使用的隔离级别
 */
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)REQUIRED传播行为
当bookService.的 purchase0方法被另一个事务方法 checkout(调用时,它默认会在现有的事务内运行。这个默认的传播行为就是REQUIRED。因此在checkoutQ方法的开始和终止边界内只有一个事务。这个事务只在checkoutQ方法结束的时候被提交,结果用户一本书都买不了。

②.REQUIRES_NEW传播行为
表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就
应该先挂起它。

  • 默认的隔离级别,有就用,没有就创建。所以:都会购买失败,事务会回滚。
  • REQUIRES_NEW,开启一个新的事务,把你的挂起,可以购买成功一本书。
    • 假如:checkout 事务为t1,purchase 事务为 t2,t3
    • t1开始,调用purchase 后,t1挂起。
      • t2开始,t2结束。t1可以继续了 在此调用 purchase 。
        • t1 又被挂起,t3开始,t3结束,t1 又可以继续,t1结束。
      • t3失败了,对 t2 事务 没有影响。
测试
class TxTest {
	
	//创建IOC容器对象
	ApplicationContext ioc = new ClassPathXmlApplicationContext("beans-tx.xml");
	
	@Test
	void testBookShopDao() {
		//获取BookDao
		BookShopDao bookShopDao = (BookShopDao) ioc.getBean("bookShopDao");
		double bookPrice = bookShopDao.getBookPriceByIsbn("1001");
		System.out.println(bookPrice);
		//更新图书的库存
		bookShopDao.updateBookStock("1001");
		//更新账户的余额
		bookShopDao.updateAccountBalance(1, bookPrice);
	}
    
    @Test
	void testCashier() {
		Cashier cashier = (Cashier) ioc.getBean("cashier");
		//创建List
		List<String> isbns = new ArrayList<>();
		isbns.add("1001");//只有100块钱,只能买这本书,下一本买不起。
		isbns.add("1002");
		//去结账
		cashier.checkout(1, isbns);
	}


	@Test //测试 打断点,读取两次结果。默认是 可重复读。
	void testBookShopService() {
		BookShopService bookShopService = (BookShopService) ioc.getBean("bookShopService");
		bookShopService.purchase(1, "1001");
	}
}
  • 数据库 余额是 Unsigned,无符号的,只能为 整数,用于 金额,就是这样用的。

    • 如果为负数,数据库会报 超出余额的范围。
项目其他无用的
  • beans-tx.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 http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<!-- 设置自动扫描的包 -->
	<context:component-scan base-package="com.atguigu.spring.tx"></context:component-scan>

	<!-- 引入外部的属性文件 -->
	<context:property-placeholder location="classpath:druid_tx.properties" />

	<!-- 配置数据源 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>

		<property name="initialSize" value="${jdbc.initialSize}"></property>
		<property name="minIdle" value="${jdbc.minIdle}"></property>
		<property name="maxActive" value="${jdbc.maxActive}"></property>
		<property name="maxWait" value="${jdbc.maxWait}"></property>
	</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"></property>
	</bean>
	
	<!-- 开启事务注解支持 
		如果事务管理器的id属性值为transactionManager,那么transaction-manager属性可以省略不写
	-->
<!-- 	<tx:annotation-driven transaction-manager="transactionManager"/> -->
	<tx:annotation-driven/>
</beans>

  • druid_tx.properties
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring_transaction
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.initialSize=10
jdbc.minIdle=5
jdbc.maxActive=20
jdbc.maxWait=5000
  • BookShopDao
@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Override
	public double getBookPriceByIsbn(String isbn) {
		// 写sql语句
		String sql = "select price from book where isbn = ?";
		// 调用JdbcTemplate中的queryForObject方法
		Double bookPrice = jdbcTemplate.queryForObject(sql, Double.class, isbn);
		return bookPrice;
	}

	@Override
	public void updateBookStock(String isbn) {
		// 写sql语句
		String sql = "update book_stock set stock = stock - 1 where isbn = ?";
		// 调用JdbcTemplate中的update方法
		jdbcTemplate.update(sql, isbn);
	}

	@Override
	public void updateAccountBalance(int userId, double bookPrice) {
		// 写sql语句
		String sql = "update account set balance = balance - ? where id = ?";
		// 调用JdbcTemplate中的update方法
		jdbcTemplate.update(sql, bookPrice, userId);
	}
}

4个隔离级别3问题

隔离级别
isolation
英
/ˌaɪsəˈleɪʃn/
n.
隔离,孤立;孤独;绝缘;(尤指对混合物或微生物的)离析
public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1), //读 未提交
    READ_COMMITTED(2), //读 已提交
    REPEATABLE_READ(4), //可重复度
    SERIALIZABLE(8);//串行执行
}
隔离级别脏读不可重复读幻读加锁读
读未提交(Read uncommitted)不加锁
读已提交(Read committed)X不加锁
可重复读(Repeatable read)XX不加锁
可串行化(Serializable )XXX加锁

读未提交:

  • 允许Transaction01读取Transaction02未提交的修改。

读已提交:

  • 要求Transaction01只能读取Transaction02已提交的修改。

可重复读:

  • 确保Transaction01可以多次从一个字段中读取到相同的值,即 Transaction01执行期间禁止其它事务对这个字段进行更新。
    尚硅谷的这个应该错了,不会禁止。

  • 即:我开事务的那一刻,就决定了 当前数据库在这个点了。随便查,都是这一刻的数据(不会被他人影响)

串行化:

  • 确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
可重复读出现幻读
  • 基本情况,t1 开启事务,t2 开启事务,t1 新增 并提交。 t2 事务中 不会查到最新的。
    • 针对如果事务 1 中只有两次 select,事务 2 在事务 1 的两次 select 之间插入了影响 select 结果的数据,事务 1 的两次 select 结果是一样的。所以可重复读的隔离级别不是完全不能隔离幻读。
  • https://blog.csdn.net/rsjssc/article/details/123465816

有两种情况无法防止幻读。

  • 一是主动用 for share 或者 for update 这样带锁的 select 语句, 对应下文实验中的现象

    • 事务1 开启事务,事务2 开启事务。
    • 事务1 插入后提交。事务2,查询(查询不到最新的),但 for update 查询可以
  • 二是 开始较早的事务更新了 开始较晚但是结束较早 的事务的新数据,

    • 那么开始较早的事务中会出现幻读,对应下文实验中的现象 2。
    • t1 开事务,t2 开事务,插入数据 提交。
      • t1 更新这条数据,t1 会更新成功(t2插入的这条)。t1提交事务。
  • MVCC多版本并发控制(Multi-Version Concurrency Control)是MySQL中基于乐观锁理论实现隔离级别的方式,用于实现读已提交和可重复读取隔离级别。

  • Oracle只支持,读已提交(默认)和 可串行化

-- mysql> SELECT @@tx_isolation; ,mysql默认 可重复读。
-- +-----------------+
-- | @@tx_isolation  |
-- +-----------------+
-- | REPEATABLE-READ |
-- +-----------------+

SET SESSION TRANSACTION ISOLATION LEVEL Repeatable read

SAVEPOINT a
ROLLBACK TO b 

start transaction;
ROLLBACK
commit; -- 提交事务后。
concurrency
英
/kənˈkʌrənsɪ/
n.
[] 并发性;同时发生
3问题1

脏读(dirty read):当一个事务读取另一个事务尚未提交的改变时,产生脏读

不可重复读(nonrepeatable read):同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生不可重复读。

  • C2中看到了 C1的修改,C2就发生了 不可重复读。

  • C2在统计10:00之前的数据,却读到了 C2在10点以后 提交的数据。

  • 主要是:修改和删除。

幻读(phantom read):同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻读。

  • 主要是:插入操作。
3问题2

假设现在有两个事务: Transaction01和 Transaction02并发执行。

  1. 脏读:当前事务,读取到了 其他事务更新,但没有提交的值。
  1. t1 将某条记录的AGE值从20修改为30。
  2. t2 读取了Transaction01更新后的值:30。
  3. t1 回滚,AGE值恢复到了20。
  4. t2 读取到的30就是一个无效的值。

2)不可重复读:当前事务,读第二次,读取到了 其他事务更新(已提交),读取两次不一致。

  1. t1 读取了AGE值为20。
  2. t2 将AGE值修改为30。
  3. t1 再次读取AGE值为30,和第一次读取不一致。

3)幻读:每次返回不同的结果集,多了数据(其他事务插入)

  • 感觉出现了 幻觉。
  1. t1 读取了STUDENT表中的一部分数据。
  2. t2 向STUDENT 表中插入了新的行。
  3. t1 读取了STUDENT表时,多出了一些行。

课外:innodb 和 myisam

1、innodb支持事务,而myisam不支持事务。
2、innodb支持外键,而myisam不支持外键。
3、innodb默认表锁,使用索引检索条件时是行锁,而myisam是表锁(每次更新增加删除都会锁住表)。
4、innodb和myisam的索引都是基于b+树,但他们具体实现不一样,innodb的b+树的叶子节点是存放数据的myisam的b+树的叶子节点是存放指针的。
5、innodb是聚簇索引,必须要有主键,一定会基于主键查询,但是辅助索引就会查询两次,myisam是非聚簇索引索引和数据是分离的,索引里保存的是数据地址的指针,主键索引和辅助索引是分开的。
6、innodb不存储表的行数,所以select count( * )的时候会全表查询,而myisam会存放表的行数,select count(*)的时候会查的很快。

总结:mysql默认使用innodb,如果要用事务和外键就使用innodb,如果这张表只用来查询,可以用myisam。如果更新删除增加频繁就使用innodb。

原文链接:https://blog.csdn.net/weixin_41832850/article/details/106063426

  • MySQL 5.1之前的版本中,默认的搜索引擎是MyISAM,从MySQL 5.5之后的版本中,默认的搜索引擎变更为InnoDB

  • MyISAM:默认表类型,它是基于传统的ISAM类型,它是存储记录和文件的标准方法。不是事务安全的,而且不支持外键。

  • InnoDB:支持事务安全的引擎,支持外键、行锁、事务是他的最大特点。

03.SpringMVC中如何解决POST请求中文乱码问题

  • GET的又如何处理呢
    • 在 tomcat 7.0,server.xml下
      • 第一个 <Connector 标签 URIEncoding = “UTF-8”

CharacterEncodingFilter

public class CharacterEncodingFilter extends OncePerRequestFilter {
    private String encoding;
    private boolean forceEncoding = false;

    public CharacterEncodingFilter() {
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public void setForceEncoding(boolean forceEncoding) {
        this.forceEncoding = forceEncoding;
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
            //请求的设置
            request.setCharacterEncoding(this.encoding);
            if (this.forceEncoding) {
                //响应的也设置
                response.setCharacterEncoding(this.encoding);
            }
        }

        filterChain.doFilter(request, response);
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        //响应的 也设置为 UTF-8
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

springmvc.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:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		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-4.0.xsd">
	
	<!-- 配置自动扫描到的包 -->
	<context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>
	
	<!-- 配置视图解析器 -->
	<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	
	<!-- 配置不经过处理器方法直接到达响应页面 
		path属性:设置要映射请求地址
		view-name属性:设置视图名
	-->
	<mvc:view-controller path="/testViewResolver" view-name="success"/>
	<!-- 配置了不经过处理器方法直接到达响应页面之后,处理器方法上的@RequestMapping将失效,此时必须配置以下标签 -->
	<mvc:annotation-driven></mvc:annotation-driven>
</beans>

index

\WebContent\index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="${pageContext.request.contextPath }/testPOJO" method="post">
		<!-- 表单项中的name属性值要与POJO类中的属性名保持一致 -->
		工号:<input type="text" name="id"><br>
		姓名:<input type="text" name="lastName"><br>
		邮箱:<input type="text" name="email"><br>
		部门编号:<input type="text" name="dept.id"><br>
		部门名称:<input type="text" name="dept.name"><br>
		<input type="submit">
	</form>
</body>
</html>

Controller

@Controller
public class SpringMVCHandler {
	
	public static final String SUCCESS="success";

	//1.SpringMVC中如何解决POST请求中文乱码问题,GET的又如何处理呢
	
	/*
	 * ★测试入参为POJO
	 * Spring MVC会按请求参数名和 POJO属性名进行自动匹配,
	 * 		    自动为该对象填充属性值。支持级联属性
	 */
	@RequestMapping("/testPOJO")
	public String testPOJO(Employee employee) {
		System.out.println("员工的信息是:"+employee);
		return SUCCESS;
	}
}

04.简单的谈一下SpringMVC的工作流程

commons-logging-1.1.3.jar
jstl.jar
spring-aop-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
standard.jar

SpringMVCHandler

@Controller
public class SpringMVCHandler {
	
	public static final String SUCCESS="success";

	//1.简单的谈一下SpringMVC的工作流程
	
	//处理模型数据方式一:将方法的返回值设置为ModelAndView
	@RequestMapping("/testModelAndView")
	public ModelAndView testModelAndView() {
		//1.创建ModelAndView对象
		ModelAndView mav = new ModelAndView();
		//2.设置模型数据,最终会放到request域中
		mav.addObject("user", "admin");
		//3.设置视图
		mav.setViewName("success");
		return mav;
	}
	/*
	 * ★处理模型数据方式二:方法的返回值仍是String类型,在方法的入参中传入Map、Model或者ModelMap
	 * 	不管将处理器方法的返回值设置为ModelAndView还是在方法的入参中传入Map、Model或者ModelMap,
	 *  SpringMVC都会转换为一个ModelAndView对象
	 */
	@RequestMapping("/testMap")
	public String testMap(Map<String , Object> map) {
		//向Map中添加模型数据,最终会自动放到request域中
		map.put("user", new Employee(1, "韩总", "hybing@atguigu.com", new Department(101, "教学部")));
		return SUCCESS;
	}
	
}

JSP

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<a href="${pageContext.request.contextPath }/testModelAndView">Test ModelAndView</a><br>
	<a href="${pageContext.request.contextPath }/testMap">Test Map</a><br>
</body>
</html>

springmvc.xml

	
	<!-- 配置自动扫描到的包 -->
	<context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>
	
	<!-- 配置视图解析器 -->
	<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	
	<!-- 配置不经过处理器方法直接到达响应页面 
		path属性:设置要映射请求地址
		view-name属性:设置视图名
	-->
	<mvc:view-controller path="/testViewResolver" view-name="success"/>
	<!-- 配置了不经过处理器方法直接到达响应页面之后,处理器方法上的@RequestMapping将失效,此时必须配置以下标签 -->
	<mvc:annotation-driven></mvc:annotation-driven>

success.jsp

WebContent/WEB-INF/views/success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>Success Page</h1>
	<h2>request域中的用户是:${requestScope.user }</h2>
</body>
</html>

流程

chain
英
/tʃeɪn/
n.
链,链条;一连串,一系列;连锁商店;镣铐;测链;连环式(指一群房主先售后购的置屋方式);<文>约束,束缚
v.
用锁链拴住;拘禁,束缚
    1. 请求过来后,到达
  • DispatcherServlet中央控制器

    1. 处理器映射器 找到 处理器
    • 处理器映射器 HandlerMapping 的方法

        1. 得到:HandlerExecutionChain 处理执行链,里面有 所有的 处理器拦截器 Handlerintercepter 和 处理器对象
      1. 得到 处理器的适配器 HandlerAdapter
        1. 找对应的处理器,就是controller 处理请求
            1. 返回 ModelAndView
    1. 返回 ModelAndView,给中央处理器
    1. 通过配置的 viewResolver视图解析器,找到视图InternalResourceView
    • InternalResourceViewResolver
        1. 返回视图InternalResourceView
    1. 渲染视图,将模型数据,给用户呈现出来。
    2. 响应用户

1、发送请求

  • DispatcherServlet中央控制器

2、调用处理器映射器找到处理器

  • HandlerMapping处理器映射器

3、返回HandlerExecutionChain 包含了所有的 拦截器和 处理器

  • Handlerintercepter(处理器拦截器)
  • Handlert(处理器对象)

4、通过处理器适配器调用具体的处理器

  • HandlerAdapter(处理器适配器)
    • 5、调用处理器
      • Handler(即controller)
    • 6、返回 ModelAndView
      • HandlerAdapter(处理器适配器)
        • 7、返回ModelAndView
          • DispatcherServlet中央控制器

8、视图解析

  • viewResolver视图解析器 InternalResourceViewResolver
    • 9、返回view
      • DispatcherServlet中央控制器

10、渲染视图

  • view视图

11、响应用户(依然是:DispatcherServlet中央控制器)

5.MyBatis属性名和表中的字段名不一样 ?

//MyBatis中当实体类中的属性名和表中的字段名不一样 ,怎么办 
/*
 * 解决方案:
 *     1.写sql语句时起别名
 *     2.在MyBatis的全局配置文件中开启驼峰命名规则
 *     3.在Mapper映射文件中使用resultMap来自定义映射规则
 */
log4j-1.2.17.jar
mybatis-3.4.2.jar
mysql-connector-java-5.1.37-bin.jar

配置文件

  • config/mybatis-config.xml
  • map Under score To CamelCase
underscore
英
/ˌʌndəˈskɔː(r)/
v.
强调;在……的下面划线
n.
(尤指为强调)下划线

score
英
/skɔː(r)
n.
(游戏或比赛中的)得分,比分;(测验的)评分,分数;
v.
(在运动、比赛或考试中)得(分);评分,打分数;

Camel
英
/ˈkæm(ə)l/
n.
骆驼;驼绒织品;驼色,浅黄褐色;打捞浮筒
adj.
驼色的,暗棕色的
<?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>
<!-- 	<settings> -->
		 <!-- 开启驼峰命名规则 ,可以将数据库中的下划线映射为驼峰命名
		 	例如:last_name可以映射为lastName
		 -->
<!-- 		<setting name="mapUnderscoreToCamelCase" value="true"/> -->
<!-- 	</settings> -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>
	<!-- 注册映射文件 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>
  • EmployeeMapper.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">
<!-- namespace属性:必须是接口的全类名 -->
<mapper namespace="com.atguigu.mybatis.mapper.EmployeeMapper">
	<!-- 
		id属性:必须是接口中方法的方法名
		resultType属性:必须是方法的返回值的全类名
	 -->
	<select id="getEmployeeById" resultMap="myMap">
		select * from employees where id = #{id}
	</select>
	
	<!-- 自定义高级映射 -->
    <resultMap type="com.atguigu.mybatis.entities.Employee" id="myMap">
    	<!-- 映射主键 -->
    	<id column="id" property="id"/>
    	<!-- 映射其他列 -->
    	<result column="last_name" property="lastName"/>
    	<result column="email" property="email"/>
    	<result column="salary" property="salary"/>
    	<result column="dept_id" property="deptId"/>
    </resultMap>
</mapper>
  • config/log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
 <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
   <param name="Encoding" value="UTF-8" />
   <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n" />
   </layout>
 </appender>
 <logger name="java.sql">
   <level value="debug" />
 </logger>
 <logger name="org.apache.ibatis">
   <level value="info" />
 </logger>
 <root>
   <level value="debug" />
   <appender-ref ref="STDOUT" />
 </root>
</log4j:configuration>

实体类和接口

public class Employee {

	private Integer id;
	private String lastName;
	private String email;
	private double salary;
	private Integer deptId;
	//get set 全参构造,toString

}
public interface EmployeeMapper {

	Employee getEmployeeById(Integer id);
}

测试类

	@Test
	void testGetEmployee() throws IOException {
		//1.创建SqlSessionFactory对象
		String resource = "mybatis-config.xml";
        
		InputStream inputStream = Resources.getResourceAsStream(resource);
        
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        
		//2.获取sqlSession,sqlSession就相当于JDBC中的connection
		SqlSession sqlSession = sqlSessionFactory.openSession();
		try {
		  //3.获取Mapper对象
		  EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            
		  //4.调用EmployeeMapper中获取Employee的方法
		  Employee employee = mapper.getEmployeeById(1);
            
		  System.out.println(employee);
		} finally {
		  //5.关闭sqlSession
		  sqlSession.close();
		}
	}

SQL

/*
SQLyog Ultimate v11.25 (64 bit)
MySQL - 5.5.28 : Database - mybatis
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatis` /*!40100 DEFAULT CHARACTER SET gb2312 */;

USE `mybatis`;

/*Table structure for table `departments` */

DROP TABLE IF EXISTS `departments`;

CREATE TABLE `departments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=gb2312;

/*Data for the table `departments` */

insert  into `departments`(`id`,`name`) values (1,'教学部'),(2,'教务部'),(3,'运营部'),(4,'咨询部'),(5,'就业部');

/*Table structure for table `employees` */

DROP TABLE IF EXISTS `employees`;

CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `last_name` varchar(100) DEFAULT NULL,
  `email` varchar(100) DEFAULT NULL,
  `salary` double(11,2) DEFAULT NULL,
  `dept_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `dept_id` (`dept_id`),
  CONSTRAINT `employees_ibfk_1` FOREIGN KEY (`dept_id`) REFERENCES `departments` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=gb2312;

/*Data for the table `employees` */

insert  into `employees`(`id`,`last_name`,`email`,`salary`,`dept_id`) values (1,'HanZong','hybing@atguigu.com',30000.00,1),(2,'Mayun','mayun@alibaba.com',20000.00,2),(3,'Mahuateng','mahuateng@qq.com',10000.00,3);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值