SSM
Mybatis+Spring
Mybatis整体架构:
创建步骤:
引入依赖(pom.xml)
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--三大连接池了解一下,选一个用,了解连接池的作用-->
<!--德鲁伊,来自于阿里的数据源(连接池)-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
全局配置文件(application.xml)
<!--配置一个数据源(连接池)-->
<!--引入的是德鲁伊连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<!-- ?characterEncoding=utf-8 为了解决数据进数据库后中文乱码的问题-->
<property name="url" value="jdbc:mysql:///myssm?characterEncoding=utf-8"> </property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
<property name="maxActive" value="20"/>
<property name="initialSize" value="1"/>
<property name="minIdle" value="1"/>
</bean>
<!--配了连接池给谁用呢?数据库用,怎么用?通过Mybatis的org.mybatis.spring.SqlSessionFactoryBean包在sql会话工厂里用,这个工厂用来干嘛的?用来操作数据库,怎么操作数据库?用sql语句操作,sql语句在哪里?Mapper.xml里-->
乱码问题1--解决数据库乱码:
?characterEncoding=utf-8 为了解决数据进数据库后中文乱码的问题
写dao接口
public interface UserDao {
public int add(User User);
// 根据id删除,为什么要传入一个对象?
public int delete(int id);
public int update(User User);
public User get(int id);
public List<User> getAll();
}
几个相关的问题:
-
- 为了后期的维护,假如修改了dao中的代码,只要编译dao中的代码,不用管servlet,因为servlet就像引用一样,dao长什么样它就怎么用;
- 为什么要用Service接口?是让表示层不依赖于业务层的具体实现。为什么要用DAO接口?是让业务层不依赖于持久层的具体实现。有了这两个接口,Spring IOC容器才能发挥作用。
-
根据id删除,为什么要传入一个对象?
- 为了方便后期维护?不想按id修改时可以按其他数据修改
-
model类为什么要实现Serializable这个接口?
-
为了JavaBean可以实现对象序列化,而实现java.io.Serializable 接口的类是可序列化的。没有实现此接口的类将不能使它们的任一状态被序列化或逆序列化。
-
对象序列化就是可以把对象存到字节流,然后可以恢复!当一个JavaBean在构造工具内被用户化,并与其它Bean建立连接之后,它的所有状态都应当可被保存,下一次被load进构造工具内或在运行时,就应当是上一次修改完的信息。为了能做到这一点,要把Bean的某些字段的信息保存下来,在定义Bean时要使它实现Java.io.Serializable接口。
-
对象序列化后用在哪里了?
-
配置Mapper.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.wangym.dao.UserDao">
<!--写增删改查,sql语句不要';'-->
<insert id="add" parameterType="User">
<!--最好不要有注释,可能会报错 #{}和${}的区别-->
<!-- #{}和${}的区别? -->
<select id="get" parameterType="int" resultType="User">
select * FROM `users` where id=#{id}
</select>
</insert>
</mapper>
如何预防sql注入的问题:#{}和${}的区别?
-
#{}是预编译处理,${}是字符替换,mybatis在处理#{}时会将sql中的#{}替换为?,调用preparedStatement通过set方法按占位符"?"先后顺序来赋值;将${}替换为里面的值 - SQL 语句被预编译并且存储在 PreparedStatement 对象中。然后只需要生成一个执行计划,就可以使用此对象高效地多次执行该语句。
CRUD里的属性
<select
id="get" statement的id或者叫sql的id 个人理解=方法名
parameterType="int" 声明输入参数的类型 参数类型
resultType="User" 声明输入结果的类型,全路径 结果集的封装类型
>
在mybatisCnfig.xml中添加配置
-
得到sqlSessionFactory,打开数据源(连接池dataSource),挂接映射文件(mapper.xml)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!--挂接映射文件--> <property name="mapperLocations" value="classpath:mapper/*.xml"/> <!--设置别名,在Mapper文件里面就可以直接写对应的类名,而不用写全路径名了--> <property name="typeAliasesPackage" value="com.wangym.model"/> </bean>-
property标签的name属性是指bean标签中class属性的类中的私有属性名,
如果该属性的值是引用的其它bean,那么就不能用value标签,而是ref标签
如果该属性的值引用了匿名内部类,则直接使用bean标签即可(不指定id或name属性)
-
ref作为属性时,就是查找当前配置文件的bean,作为标签时就是寻找全局中的 bean
-
写service实现dao
-
dao类是接口,
Service implements Dao就要@Overridedao的方法,显然没有比在dao里直接写简便,所以要实现dao接口不用implements,而是用注解 -
其实dao的实现类,就是dao的bean是通过扫描组建生成的
<!--dao的扫描器,生成dao的实现类--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--配的是所有dao接口所在包名--> <property name="basePackage" value="com.wangym.dao"/> </bean> -
@Service public class UserService implements UserDao { @Resource private UserDao userDao; }-
- 如果一个类带了@Service注解,将自动注册到Spring容器,不需要再在applicationContext.xml配置文件中定义bean了,类似的还包括@Component、@Repository、@Controller
-
-
注入方式不同:
@Resource先按名字不行再按类型注入,
@Autowired只能按类型注入,要按名称就得结合@Qualifier注解一起使用
-
提供方不同:前者是J2EE提供,后者是spring提供
-
-
-
在applicationContext.xml配置文件中打开组件扫描配置
<context:component-scan base-package="com.wangym"/>
写UserDao的测试类
- 在src文件夹下新建
test.java.com.wangym.service.UserDaoTest
写控制器类
写前端页面
运行报错:
-
java.lang.IllegalStateException: Failed to load ApplicationContext详细:Error creating bean with name XXX: Injection of resource dependencies failed
原因:web.xml没有配置spring的上下文环境和核心的监听器
-
javax.servlet.ServletException: Servlet.init() for servlet [springmvc] threw exceptionBeanCreationException: Error creating bean with name 'userController': Injection of resource dependencies failed;详细:Servlet.init()初始化错误,错误一般发生在控制器中,当出现重名的servlet时候也会报类似的错误
原因:controller中出现了,不存在的控制器引用
例如:return “redirect:/admin/query.action”,返回重定向到另外一个控制器,但是控制器未定义,就会出现上述错误!
applicationContext.xml配置超详细
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p">
<!-- 配置组件包扫描的位置 -->
<context:component-scan base-package="cn.sxt" />
<!-- 读取db.properties配置文件到Spring容器中 -->
<context:property-placeholder
location="classpath:db.properties" />
<!-- 配置 阿里巴巴的 druid 数据源(连接池) -->
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<!-- SpringEL 语法 ${key} -->
<property name="driverClassName"
value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<!-- ${username}如果key是username,name 默认spring框架调用的当前操作系统的账号 解决方案:可以统一给key加一个前缀 -->
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="${jdbc.maxActive}" />
</bean>
<!-- 创建SqlSessionFactory MyBatis会话工厂对象 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 读取映射文件 ,MyBatis的纯注解不用配置 -->
<property name="mapperLocations">
<array>
<!-- 配置单个映射文件 -->
<!-- <value>classpath:cn/zj/ssm/mapper/UserMapper.xml</value> -->
<!-- 配置多个映射文件使用 * 通配符 -->
<value>classpath:cn/sxt/mapper/*Mapper.xml</value>
</array>
</property>
<!-- 配置mybatis-confg.xml主配置文件(注配置文件可以保留一些个性化配置,缓存,日志,插件) -->
<property name="configLocation"
value="classpath:mybatis-config.xml" />
<!-- 配置别名,使用包扫描 -->
<property name="typeAliasesPackage" value="cn.sxt.pojo"></property>
</bean>
<!-- SqlSession 不用单独创建,每次做crud操作都需要Mapper接口的代理对象 而代理对象的创建又必须有 SqlSession对象创建
Spring在通过MyBatis创建 Mapper接口代理对象的时候,底层自动把SqlSession会话对象创建出来 -->
<!-- 创建UserMapper接口的代理对象,创建单个代理对象 参考桥梁包:org.mybatis.spring.mapper.MapperFactoryBean<T>
此类就是创建 Mapper 代理对象的类 -->
<!--<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
注入UserMapper接口 <property name="mapperInterface" value="cn.sxt.mapper.UserMapper"/>
注入sqlSessionFactory工厂对象 <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean> -->
<!-- 使用包扫描创建代理对象,包下面所有Mapper接口统一创建代理对象 使用桥梁包下面 : org.mybatis.spring.mapper.MapperScannerConfigurer
可以包扫描创建所有映射接口的代理对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 配置SqlSessionFactoryBean的名称 -->
<property name="basePackage" value="cn.sxt.mapper"/>
<!-- 可选,如果不写,Spring启动时候。容器中。自动会按照类型去把SqlSessionFactory对象注入进来 -->
<!-- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
2.配置事务的细节
配置事务通知/增强
-->
<tx:advice id="tx" transaction-manager="transactionManager" >
<!-- 配置属性 -->
<tx:attributes>
<!-- DQL -->
<tx:method name="select*" read-only="true" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="5"/>
<tx:method name="query*" read-only="true" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="5"/>
<tx:method name="get*" read-only="true" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="5"/>
<tx:method name="find*" read-only="true" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="5"/>
<!-- 其他 -->
<tx:method name="*" read-only="false" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="5"/>
</tx:attributes>
</tx:advice>
<!--
3.使用AOP将事务切到Service层
-->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* cn.zj.ssm.service..*.*(..))" id="pt"/>
<!-- 配置切面= 切入点+通知 -->
<aop:advisor advice-ref="tx" pointcut-ref="pt"/>
</aop:config>
</beans>