欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring整合MyBatis框架

程序员文章站 2022-07-13 21:13:47
...

mybatis

  1. 动态sql
<foreach collection="要遍历的集合" open="起始符号" close="结束符号" separator="分隔符" item="临时变量名">
<if test="条件">sql片段</if>
<where> 去掉多余and
<set>   去掉多余的,
  1. 分页
    2.1) 物理分页 使用sql语句进行分页(mysql limit), 分页sql不通用, 效率高
    2.2) 逻辑分页 全部查询,sql简单通用, 效率低,只适用于数据量少的情况
    sqlSession.selectList(“sql id名”, new RowBounds(下标, 大小));

  2. 数据库与实体类不一致的情况
    first_name
    firstName
    方法1: 在select语句中使用列别名, 只要让别名与属性对应
    方法2:

<select resultMap="id值" ...>
<resultMap id="">
	<id column="主键列名" property="属性名">
	<result column="列名" property="属性名">
</resultMap>
  1. 连接查询的映射(复杂的结果映射)
    select … from product p inner join category c on p.category_id=c.id;

class Product {
id name…
private Category category; // id,name… 关系属性
}

class Category {

}

<resultMap id="" type="Product">
	<id column="主键列名" property="属性名">
	<result column="列名" property="属性名">
	<assoiation property="category" javaType="Category">
		<id>
		<result>
	</assoiation>
</resultMap>
  1. 插入数据时

    可以用来把自增列生成的主键值赋值给实体类的属性

  2. #{} ${}
    #{} 生成? 占位符, ${} 是直接拼接字符串,有注入攻击风险
    #{} 不能运算, ${} 可以运算
    #{} 只能替换值,不能替换(表名,列名,关键字), 而${}都可以

==========================================================================

spring框架

用途

  1. 整合其他框架,让他们协同工作
  2. 提供了声明式的事务管理 – 通过配置(xml, 注解)来管理事务

两大核心思想:IOC,AOP

1. IOC (控制反转)

class servlet extends HttpServlet{
init()
service()
destroy()
}

创建不是程序员来管的 --> tomcat容器
类中的某些方法也不需要我们自己调用 --> 由tomcat来调用

控制反转(Inversion of controll):把对象的创建以及某些方法的调用交由容器来管理
spring容器,类似于tomcat容器,但它能容纳的对象类型更为丰富:dao,service,controller

class UserDao {

}

UserDao dao = new UserDao(); // 原来我们需要自己创建对象
UserDao dao; // spring会创建好

2. 使用步骤

  1. 在pom.xml文件中加入spring依赖
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>4.3.18.RELEASE</version>
</dependency>
  1. 编写spring的配置文件, 并定义要控制反转的类
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--受spring容器管理类, 使用<bean>标签管理     
    id="唯一标识"
    -->
    <bean id="userDao" class="com.westos.dao.UserDao">
        
    </bean>
</beans>
  1. 使用spring容器
// 1. 创建spring容器
// 根据xml文件应用程序Context容器(上下文)
// classpath指配置文件的位置, 起点有java, resources. 写路径时相对这个起点去写
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("abc/spring.xml");

// 2. 使用容器内的对象
//        UserDao dao = (UserDao)context.getBean("userDao"); // 根据id获取

UserDao dao = context.getBean(UserDao.class);// 根据类型获取, 找一个类型为UserDao的对象
dao.insert();

注意:

  • bean id是严格区分大小写的 如果id不存在会 NoSuchBeanDefinitionException
  • 如果容器中同一个类型的bean有多个,再根据类型获取的话 NoUniqueBeanDefinitionException
  1. bean一般需要无参构造,spring会调用它来创建对象
    如果没有无参构造,需要给构造方法的参数赋值:
<constructor-arg index="参数下标" value=""/>
  1. 控制对象个数
    默认情况下,每个类型的bean在容器中只有一个对象(单例)

多例,每用一次,创建一个新的对象
如果配置多例<bean scope="prototype">

  1. 初始化以及销毁方法
    <bean init-method="初始化方法名" destroy-method="销毁方法名">
    单例对象既会调用初始化,也会调用销毁方法
    多例对象,每使用一个多例对象,会调用初始化方法,但所有多例对象都不会调用销毁方法

  2. 依赖注入(看做控制反转的一个补充)
    tomcat和spring容器都实现了控制反转, 但spring容器有依赖注入的功能,而tomcat没有
    DI (dependency injection)

给bean的属性赋值的过程,称为依赖注入,是由spring来管理bean与bean之间的依赖关系

<property name="要注入的属性名" ref="要依赖的bean 的id值"/>
  1. 依赖注入的三种方式
    方式1: set注入
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
    }
<property name="要注入的属性名" ref="要依赖的bean 的id值"/>

方法2: 构造注入
public UserService(UserDao userDao) {
this.userDao = userDao;
}

<constructor-arg index="0" ref="userDao"/>

方法3: 注解注入
@Autowired(spring提供的) 利用这个注解完成属性的赋值,把它加在需要注入的属性上, 或set方法上,或构造方法上

要启用该注解

<context:annotation-config/>

@Resource (java官方的注解) 用法与@Autowired类似

  1. 可以支持将*.properties 注入到spring.xml中
<!--读取properties 文件 location="文件位置"  placeholder 占位符-->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 可以利用 ${key} 获取 *.properties 文件中的值-->
<bean id="productDao" class="com.westos.dao.ProductDao">
	<property name="username" value="${jdbc.username}"/>
	<property name="password" value="${jdbc.password}"/>
</bean>

注解方式 @Value 可以完成这种值注入
@Value("${properties中的key}")

要保证spring.xml的配置文件不要修改,要修改的内容可以配置到*.properties文件中

  1. 整合mybatis
    步骤1:在pom.xml加入依赖
    mybatis, mysql, spring-context, logback, druid, junit, spring-jdbc, mybatis-spring

步骤2:把关键对象spring控制反转
连接池对象, sqlSessionFactory, sqlSession

<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 1) 把数据源对象交给spring容器管理 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
	<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="${jdbc.max}"/>
	<property name="minIdle" value="${jdbc.min}"/>
</bean>
<!-- 2) sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 注入连接池 -->
	<property name="dataSource" ref="aaa"/>
	<!-- 注入mapper.xml文件的位置-->
	<property name="mapperLocations" value="classpath:com/westos/mapper/*.xml"/>
</bean>
<!-- 3) sqlSession, 用SqlSessionTemplate得到的SqlSession可以不用我们自己操心事务的管理,以及关闭操作 -->
<bean id="sql" class="org.mybatis.spring.SqlSessionTemplate">        
	<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

步骤3:
使用SqlSession工厂

ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("abc/spring-mybatis.xml");
SqlSession sqlSession = context.getBean(SqlSession.class);
Map<String, Object> map = new HashMap<String,Object>();
map.put("m", "0");
map.put("n", 5);
List<Product> list = sqlSession.selectList("com.westos.mapper.ProductMapper.selectByPage", map);
for (Product product : list) {
	System.out.println(product);
}

1. 通过接口mapper来管理sql语句

spring.xml配置文件

<!-- 3) sqlSession工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 4) 搜索有哪些mapper接口, 把每一个mapper接口配置成spring中的一个bean -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	<!-- (起始)包名, 从这个包开始扫描-->
	<property name="basePackage" value="com.westos.mapper"/>
</bean>

接口:

public interface ProductMapper {
    // 实现类由mybatis自动生成(动态代理)
    @Insert("insert into product(name,price) values(#{name}, #{price}) ")
    void insert(Product product);

    @Update("update product set name=#{name}, price=#{price} where id=#{id}")
    void update(Product product);

    @Delete("delete from product where id=#{id}")
    void delete(int id);

    @Select("select * from product where id=#{id}")
    Product findById(int id);

    @Select("select * from product limit #{m}, #{n}")
    List<Product> findByPage(Map<String,Object> map);

    @Select("select * from product")
    List<Product> findByPage2(RowBounds rowBounds);

    @Select("select count(*) from product")
    int findCount();
}

接口方式的mapper,比较适合写基本和简单的sql,如果遇到复杂的sql(例如动态sql,表连接映射等),可以借助原来的xml mapper

接口mapper和xml mapper结合:

  1. 目录结构要一致
  2. xml中 namespace的取值与接口的包名类名要一致
  3. 接口中的方法名与xml中标签id对应
  4. 接口中的方法名不能重复(不支持重载)
    会报错误:java.lang.IllegalArgumentException: Mapped Statements collection already contains value for xxxx

mybatis的mapper映射中,默认方法最多只接收一个参数, 多个参数需要用map或list等集合包装

要突破这个限制需要使用@Param注解,把它加在方法参数上,用它建立方法参数和sql中#{}之间的联系:

@Select("select * from product limit #{m}, #{n}")
// *.java -> *.class 方法的参数名信息会丢失,所以再按m名称去找无法找到
List<Product> findByPage3(@Param("m") int x, @Param("n") int y); 

2. maven的一些常用设置

  1. 修改项目的默认jdk版本(默认是1.5 )
    加在pom.xml的最后
<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>3.7.0</version>
			<configuration>
				<source>1.8</source>
				<target>1.8</target>
			</configuration>
		</plugin>
	</plugins>
</build>
  1. 对整个项目字符编码的设置
    写在dependencies之前
<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

3. spring 中的声明式事务管理

transaction -> tx
步骤1:启用事务

注解驱动的方式来管理事务
<tx:annotation-driven/>

步骤2:配置事务管理器

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

步骤3:使用 @Transactional
它可以加在方法上或者类上
加在方法上,表示此方法受事务管理
加在类上,那么这个类中的所有方法都受事务管理

3.1 @Transactional注解详解

默认情况下,只有方法出现的 是RuntimeException或Error以及它们的子类时(未检查异常),才会导致事务回滚

如果要改变默认情况
@Transactional(rollbackFor=异常类.class)
那么方法如果出现了该异常,或该异常的子类异常时,就会回滚

@Transactional(noRollbackFor=异常类.class)
当遇到这种异常时,不会回滚事务

最后要注意的是,在业务方法中不要自己try-catch捕获异常,否则spring无法自动回滚事务

@Transactional(isolation = 隔离级别)

@Transactional(timeout = 超时时间(秒))

@Transactional(readOnly = true|false) true表示只读(只有查询) false(会有增删改)
设置为true,性能会有所提升,但是底层数据库驱动支持(对mysql支持)

@Transactional(propagation = 传播行为)
REQUIRED (默认值) required 必须的 (如果没有事务,开始新的;如果有了用已有的)
SUPPORTS 支持的 (如果没有事务,不开始新的;如果有了用已有的)
REQUIRES_NEW 需要新的 (总会开始新事务)

	只有两个业务类的方法相互调用时,传播行为才会有效
	ProductService 商品业务类
@Transactional(propagation=REQUIRED) 
biz1() { // 事务1
			biz2()
		}
    OrderService   订单业务类	
		@Transactional(propagation=REQUIRES_NEW)
	    biz2(); // 事务2