《俗人笔记》之《mybatis与spring的自我小结》

框架的真谛总结:非侵入式编程,及不在源代码上修改,也能实现效果,这里是动态代理的绝佳展示,还有就是集中管理

mybatis的期望:减少sql语句与java代码的耦合,sql就干sql,所以才用mapper配置文件替代接口的实现类,为了让mapper配置文件与接口连接,所以使用动态代理,这样做不但可以减少耦合,还便于集中管理,

spring的期望:减少各层间的耦合度,通过工厂模式+反射+xml来实现,工厂在于创建对象,反射在于根据类来返回更具通用性的object,这些都交给spring就行

mybatis

mybtis与传统的区别:传统dao执行数据库,都是通过接口要求来对应实现功能并书写对应的sql语句,在mybatis中就可以通过sqlseesion来实现crud,可以
自己定义sql语句

探索一:创建mapper文件,并在里面按照mybatis的规范写数据执行语句,然后将原先的实现类改换成调用mapper里面方法,这里中介是sqlseesion
问题:这里的sqlssesion怎么获取,通过sqlseesion工厂来获取,其实通过各种方法自己手动传入或者是就在实现类中就创建都一样,这里就
构造方法传入吧

private SqlSession sqlSession;
   	 public UserDaoImpl(SqlSession sqlSession) {
        	this.sqlSession = sqlSession;
   	 }
	//通过sqlseesion获取对应mapper的方法
	 sqlSession.insert("UserDaoMapper.addUser", user);
        	sqlSession.commit();
	
	获取:
//指定mybatis的核心配置文件
       	 String source="mybatis-config.xml";
        	//通过mybatis提供的工具类读取配置文件
        	InputStream resourceAsStream = Resources.getResourceAsStream(source);
       	 //产生sqlseesion工厂对象
        	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
       	 //获取会话对象
        	SqlSession sqlSession = sqlSessionFactory.openSession(true);//加入true可以自动提交事务,否则无法执行
        	userDao = new UserDaoImpl(sqlSession);

问题又出现了:每次执行时都会创建一个对象,而且是通过接口的实现类里面再通过sqlseesion回去对应mapper文件的方法,真他妈多此一举!
所以首先要干掉接口的实现类,并且希望直接通过接口来连接mapper文件,就相当于mapper此时就是接口的实现类
解决方法:
之前的mapper的工作空间可以乱写,但此时就不行了,此时空间名必须是接口的全路径,要让接口与之形成映射,及此时的各方法id就是接口方法名,再通过接口来调用方法时,就可以用动态代理技术了(这里是怎么使用动态代理的?所谓动态代理,这里是jdk代理,主要有代理接口),将mapper文件直接让sqlseesion读取,然后接口直接获取就行

探索二
private UserMapper mapper;//此时的usermapper就是接口,只不过换了个名字而已(为什么要用接口,这里就是面向接口编程思想了)

SqlSession sqlSession = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")).openSession(true);
        	mapper = sqlSession.getMapper(UserMapper.class);

技术核心
resultmap:
对返回值的规范,以前直接是resultype,这有很大的局限性,即只能是对象类和基本类型,但问题来了,如果出现实体类里面的属性与数据库表不一致怎么办,或者
是如果返回值很混乱怎么办(就是多表联查时)

//一对一表关系
		<resultMap id="oneToOne" type="Order" autoMapping="true">
        		<result property="id" column="id"/>//这里这样写是为了避免多表查询时id混乱,这里明确一下,这里其实也是解决匹配不一致的方法,在这里明确一下就行
        		<association property="user" javaType="User" autoMapping="true">
            		<result property="id" column="user_id"></result>
       		 </association>
    		</resultMap>

		//在上面基础上又一对多
		<resultMap id="oneToMany" type="Order" autoMapping="true" extends="oneToOne">
       		 <collection property="orderdetails" javaType="list" ofType="Orderdetail" autoMapping="true">
            		<result property="id" column="orderdetail_id"></result>//这里其实要注意了,这里的orderdetail_id其实在原查询中是没有的,因为在order表中没有其对应,orderdetail的id也不是外键
		,这就很尴尬了,所以查询时自己自定义下
        		</collection>
    		</resultMap>
		<select id="selectOrderWithUserDetailsItemByOrderNumber" resultMap="manyToMany">
		select * ,tod.id as orderdetail_id from tb_order tor left join tb_user tu on tor.user_id = tu.id
 		left join tb_orderdetail tod on tod.order_id = tor.id
		 left join tb_item ti on tod.item_id = ti.id
 		where order_number = #{orderNumber}
    		</select>

		//多对多
		<resultMap id="manyToMany" type="Order" autoMapping="true" extends="oneToOne">
        		<collection property="orderdetails" javaType="list" ofType="Orderdetail" autoMapping="true">
            		<result property="id" column="orderdetail_id"></result>
            		<association property="item" javaType="Item" autoMapping="true">
               		 <result property="id" column="item_id"></result>
            		</association>
        		</collection>
    		</resultMap>

懒加载:
	就是按需加载,虽然是都可以在一起查询,但是可以在需要时再拼接查询
 <!--开启延迟加载功能-->
        		<setting name="lazyLoadingEnabled" value="true"/>
        		<!--关闭立即加载-->
        		<setting name="aggressiveLazyLoading" value="false"/>
		<resultMap id="lazyLoad" type="Order" autoMapping="true">
		<result property="id" column="id"></result>
        		<association property="user" javaType="User" select="selectUserById" column="user_id">
        		</association></resultMap>
    		<!--延迟加载,根据订单号查询订单信息以及下单人信息-->
    		<select id="lazySelectOrderUserByOrderNumber" resultMap="lazyLoad">
       		select * from tb_order where order_number = #{orderNumber}
    		</select>
    		<select id="selectUserById" resultType="User" >
       		 select * from tb_user where id = #{user_id}
    		</select>

小注意
常规查询返回值为list时,可以resultType直接返回对应实体类型,但在多表联查时,如果是一对多,其对应属性肯定是list关系,所以在javaType中要先写list,在oftype中再写对应的实体类

懒加载时,在实体对应属性时,相较之前还是有区别的,比如,一对一之前是
现在变为 此时的select就是懒记载对象,column就是懒加载查询条件值

mybatis的配置文件属性是有顺序的,从上到下是:properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,environments?,databaseIdProvider?,mappers?

spring

传统配置:
减少每次调用时new对象,减少各层之间的依赖性,避免因为依赖层的改动而都改动(比如父接口改动的话,其实现类都要改动,因为子类可以多实现及对应多个接口,在这里想解决的问题是,根据不同的接口,希望只通过一个方法将子实现类的方法给不同接口,这里就是反射了,获得子类的类对象,返回object对象,即可自定义接口对象,此时父接口创建对应实现类,取到对应后bean后,之后要换实现类的话直接在配置文件的bean地址就行)
总结:
交给spring工厂的好处有两个:反射的使用有利于实现类更改后,可以按需求返回对应接口对象,因为它返回的object,具体自己配,这样做便于通用性,为了实现类交换容易,通过配置文件直接更改bean地址就行,这样解决硬编码,实现动态传入,即通过一个getbean("")方法传入不同参数从而获得不同实现类
拓展:
工厂模式:只为获得所需实例,不在意过程可以依次往上提升,最终都到application中配置对应的bean,spring工厂的作用就是代理创建对象和管理依赖

<bean class="dao.userDaoImpl" id="userDao"/>
   	 <bean class="service.userServiceImpl" id="userService">
       	 <property name="userDao" ref="userDao"></property>
    	</bean>
此时仍要在servieImpl里面加入dao的set方法

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  	 /* userService userService = (service.userService) applicationContext.getBean("userService");*/ 这里实通过id来获取的
    	userService userService = applicationContext.getBean(service.userService.class);//这里则是通过父接口类来获取的,便于扩展,但前提是只能有一个实现类,要不然无法对应
    	userService.login();

注解配置:
更为方便的配置,比如可以不用配置bean和依赖,
配置bean
@Component(“springService”)//可以省略括号
@Control(“springControl”)//可以省略括号,功能一样,只是换了个名字而已
@Service(“springService”)//可以省略括号,功能一样,只是换了个名字而已,便于区别
@Responlity(“springDao”)//可以省略括号,功能一样,只是换了个名字而已

配置依赖
@Autowired
@Qualifier(“dao”)//如果出现多实现,则通过id找

传统和注解可以混合配置,比如bean自己配,依赖注解,但反过来就不行

监听器
就是将读取applicationContext的任务和值交给spring和servletContext,不用每次都创建
//	传统读取,但每次都要new来创建,有点浪费,希望都公用一个就行,这时就考虑到融汇项目的servletCongtext
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

//先到web.xml里面配置

<!--spring监听器监听web容器的启动,并且创建spring容器以属性的方式绑定到servletContext中-->
 <listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
	<context-param>
   	 <param-name>contextConfigLocation</param-name>
    	<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
//通过上诉配置,项目在开启时就已经创建一个spring工厂了,并加入servletContext中,只要直接获取就行,但还是太长了
    	ApplicationContext applicationContext = (ApplicationContext) this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

//这里就直接使用spring提供好的工具类就行
    	//ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
    	TestService testService = (TestService) applicationContext.getBean("testService");
  	testService.dodo();

aop
核心属性:
tagert:对象 拦截点:对象里面的所有方法 切点:所要修改的方法 通知:就是拦截到的切点执行的操作 切面:切点+通知 织入:整个代理的过程
动态代理:
jdk代理(前提是有代理接口)

return Proxy.newProxyInstance(target.getClass.getClassLoder,target.getClass.getInterfaces,new InvocationHandler(){
	@Override
           	 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("addInfo")) {
                    System.out.println("注意啦!");
                }
                Object invoke = method.invoke(target, args);//调用原方法,执行并返回
                return invoke;})

cglib代理(无代理接口)

首先要实现MethodInterceptor接口,从而实现invoke方法
	Enhancer e=new Enhancer();//获得代理器
	//获得目标
	e.setSupperClass(target.getClass)
	//处理方法
	e.setCallBack(this)
	//生成代理对象并返回
	e.create()
	
集体方法执行代码,参数和jdk的差不多
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
   	 if (method.getName().equals("addInfo")) {
        System.out.println("大注意啦!");
    	}
    	return method.invoke(target,objects);
 }

配置:
配置目标
配置通知
配置aop
旧aop是可以对方法动态代理的
<aop:config>
//配置切点
<aop:pointcut id="mucut" expression=(bean("*Service"))/>
//配置切面
<aop:advistor advice-ref="切面id" pointcut-ref="mycut"/>
</aop:config>


aspect代理可以执行更多
	<aop:config>
	<aop:aspect ref="切面id">
	//配置切点
	<aop:pointcut id="mucut" expression=“bean("*Service")”/>

前置通知:before 参数:joinpoint  对权限日志拦截
<aop:before method="" pointcut-ref="" />
<aop:before method="" pointcut=“bean("*Service")” />//这也是一种写法,但不太好,不便于统一管理

后置通知:after 参数:joinpoint ,object 对方法执行后有返回值
<aop:after-returning  method="" pointcut-ref="" returning="object"/>

最终通知:after 无参 释放资源
<aop:after method="" pointcut-ref="" />

环绕通知:around 参数:proceedingpoint 可以执行动态代理
<aop:around method="" pointcut-ref="" />

抛出通知:after-throwing 参数:joinpoint,throwable 对方法问题抛出
<aop:after-throwing  method="" pointcut-ref=""  throwing="thorowable"/>
</aop:aspect>
</aop:config>

ioc
创建:反射
赋值:
aop:切面对象
切点:默认jdk代理拦截类,只可以用父接口方法,可以改成cglib代理实现类

注解配置
在通知类上注解配置bean,还要额外加个@Aspect,在xml配置文件中再打开<aop:aspect-autoproxy proxy-target-class=“true”/>,打开cglib代理

jdbctemplate
法一:
new DriverManagerDataSource()获得datasource,再new jt
法二:
在dao曾set注入
前提:先配内置数据源,bean的类是DriverManagerDatasource
再配bean类是jdbctemplate,再内配属性datasource
最后将bean类为dao内配置属性template

自动注入
不用dao的bean内配置了

注意: 这两个方法是在不继承jdbcDaoSupper

法三:继承jdbcdaosupper,直接调用getJT就行
但是要再dao的bean内配置属性datasouce,因为继承jdbcsupper了嘛

事务通知
开启扫描<context:compent-scan base-package=""/>
开启数据事务管理平台
bean类是datasourcethransaction,再将datasource属性内配置

配置通知
<tx:advicen id="txAdvice">
<tx:attributes>
<tx:method name="" />
</tx:attribute>
</advice>

配置切面
<aop:config>
切点
<aop:pointcut id="mycut" experssion="bean(*Service)"/>
配置通知
<aop:advistor advice-ref="txAdvice"  pointcut-ref="mycut"/>
</aop:config>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值