Knight-Wu / articles

博客
3 stars 1 forks source link

spring notes #2

Closed Knight-Wu closed 6 years ago

Knight-Wu commented 6 years ago

以下内容均参考 spring-framework-reference/core/beans-annotation-config

classpath*: / 与 classpath:/

build project

前后台传输数据

  1. 前台需要把对象通过JSON.stringfy转换json字符串,后台在参数对应加上@RequestBody,并且Request的content-type必须为json,不然会出现415的错误.

  2. 后台传输少量数据到前端,避免新增类文件的做法:

Map<String,Object> map = new HashMap<String,Object>();
map.put("leader",ifLeader);
map.put("teamId",teamId);
jsonResult.setData(new JSONObject(map));
  1. 日期类参数
    • 如果controller的dto和后台数据映射dto为同一个,且日期类使用java.util.Date作为转换类型,则在该参数上加两个标签,省掉格式转换和非空判断,@DateTimeFormat为接受前台数据的转换,@JSONField为后台给前台返回数据的转换。
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JSONField(format="yyyy-MM-dd HH:mm:ss")
Date startTime
  • spring-servlet.xml需要加以下配置
<mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <property name="supportedMediaTypes" value="application/json"/>
            <!--设置fastjson特性-->
            <property name="features">
                <array>
                    <!--设置null值也要输出,fastjson默认是关闭的-->
                    <value>WriteMapNullValue</value>
                    <!--设置使用文本方式输出日期,fastjson默认是long-->
                    <value>WriteDateUseDateFormat</value>
                </array>
            </property>
        </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

mybatis返回map

mapper:

<resultMap id="teamResultMap" type="com.mucfc.result.QueryTeamResult">
    <id column="id" property="teamId" jdbcType="INTEGER" javaType="int" />
    <result column="name" property="teamName"  jdbcType="VARCHAR" javaType="String"/>
</resultMap>
<select id="queryTeam"  resultMap="teamResultMap"  parameterType="int" >
    select id,name  from tb_dept WHERE parent= #{level}
</select>

// daoMybatis:
public List<QueryTeamResult> queryTeam(int level){
    return selectList("WeeklyMapper.queryTeam",level);
}

jetty启动时修改资源文件,可以通过update更新

修改/${jetty_home}/etc/webdefault.xml 将true改为false 并在pom文件中配置

<init-param>
  <param-name>useFileMappedBuffer</param-name>
  <param-value>false</param-value>
</init-param>
pom.xml
<plugin>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <configuration>
  <webAppConfig>
  <contextPath>/{project.build.finalName}</contextPath>
  </webAppConfig>
  <scanIntervalSeconds>3</scanIntervalSeconds>
  <httpConnector>8080</httpConnector>
  <webAppXml>jetty.xml</webAppXml>
  <webApp>
  <defaultsDescriptor>webdefault.xml</defaultsDescriptor>
  </webApp>
  </configuration>
</plugin>

httpServletResquest

spring @value

mybatis

<if test="filedName != null">
 #{fieldName}
</if>
 // 不要加非空字符串判断

spring AOP

代理技术可以说是封装了切面类(功能加强类)的逻辑和目标类的逻辑, 解耦两者的代码, 让功能加强类的逻辑能无缝插入目标类, 不产生浸入式代码.
因为以下两种原生代理, 存在以下几个问题: 
1. 不能对目标类的某些方法进行选择后进行切面
2. 还是通过硬编码的方式在代理类中, 在目标方法前后进行切面
3. 对每个类都要创建代理类, 无法实现通用
因此spring aop应运而生. 

// spring application.xml config

// 下面这种方式用于CGLIB, 可以切面类的非接口方法, 否则上面那种基于JDK的代理只能切接口方法

@Aspect // 注意需要依赖注入 @Component public class EmployeeAspect {

// 注意方法()内有两个点, 代表任意参数
@Pointcut("execution(* com.journaldev.spring.service.*.get*(..))")
public void pointCut(){
    System.out.println("enter pointcut");
}

@Before("pointCut()")
public void before(){

}

}


* 切面进入的条件
public , non static , non final ,called by class outside(非必须)
若要在类的内部调用中执行切面, 例如类里面有两个方法A, B, 在A调用B的时候进入切面, 则需要如下写法:
// 注意该类需要由spring管理,并生成, 若是在外部 Test t = new Test(); t.methodA();或者 t.methodB(); 均不能进入切面
@Component
public class Test{
@Resource Test test
    public void methodA(){
        // something
        test.methodB();
        // something
    }

    public void methodB(){}

}

### spring事务

---
* 数据并发问题
* 脏读(dirty read), A事务读到了B事务尚未提交的数据, 可理解为读到了脏数据, 若此时B事务回滚, 则会产生数据不一致的情况.

Transaction 1 Transaction 2 / Query 1 / SELECT age FROM users WHERE id = 1; / will read 20 / / Query 2 / UPDATE users SET age = 21 WHERE id = 1; / No commit here / / Query 1 / SELECT age FROM users WHERE id = 1; / will read 21 / ROLLBACK; / lock-based DIRTY READ /


* 不可重复读(unrepeatable read)
    因为select可能不需要锁, 或者发生在两次select锁获取之前, 就会读到不可重复读取的数据, 根据不同的隔离级别, 会有不同的数据. 
> At the SERIALIZABLE and REPEATABLE READ isolation levels, the DBMS must return the old value for the second SELECT. At READ COMMITTED and READ UNCOMMITTED, the DBMS may return the updated value; this is a non-repeatable read.

* 有两种策略来处理此种情况
    1. 延迟事务2的执行直到事务1提交或者回滚, 等于说维护一个时序: serial schedule T1, T2. 
    2. 为了获取更好的性能,让事务2被先提交, 事务1的执行必须满足时序 T1, T2, 若不满足事务1被回滚.

在不同模型下, 结果会不一样
> Using a lock-based concurrency control method, at the REPEATABLE READ isolation mode, the row with ID = 1 would be locked, thus blocking Query 2 until the first transaction was committed or rolled back. In READ COMMITTED mode, the second time Query 1 was executed, the age would have changed.

> Under multiversion concurrency control, at the SERIALIZABLE isolation level, both SELECT queries see a snapshot of the database taken at the start of Transaction 1. Therefore, they return the same data. However, if Transaction 1 then attempted to UPDATE that row as well, a serialization failure would occur and Transaction 1 would be forced to roll back.

> At the READ COMMITTED isolation level, each query sees a snapshot of the database taken at the start of each query. Therefore, they each see different data for the updated row. No serialization failure is possible in this mode (because no promise of serializability is made), and Transaction 1 will not have to be retried.(这段怎么跟之前说的 read commited 不一样 ? )

* 幻象读(Phantom reads),是不可重复读的特例, 相当于是范围读.
Transaction 1                                           Transaction 2

/ Query 1 / SELECT FROM users WHERE age BETWEEN 10 AND 30; / Query 2 / INSERT INTO users(id,name,age) VALUES ( 3, 'Bob', 27 ); COMMIT; / Query 1 / SELECT FROM users WHERE age BETWEEN 10 AND 30; COMMIT;

> Note that Transaction 1 executed the same query twice. If the highest level of isolation were maintained, the same set of rows should be returned both times, and indeed that is what is mandated to occur in a database operating at the SQL SERIALIZABLE isolation level. However, at the lesser isolation levels, a different set of rows may be returned the second time.

> In the SERIALIZABLE isolation mode, Query 1 would result in all records with age in the range 10 to 30 being locked, thus Query 2 would block until the first transaction was committed. In REPEATABLE READ mode, the range would not be locked, allowing the record to be inserted and the second execution of Query 1 to include the new row in its results.

* 以上均参见[wiki isolation](https://en.wikipedia.org/wiki/Isolation_(database_systems)#Dirty_reads)

Transaction 1 Transaction 2 / Query 1 / SELECT FROM users WHERE id = 1; / Query 2 / UPDATE users SET age = 21 WHERE id = 1; COMMIT; / in multiversion concurrency control, or lock-based READ COMMITTED / / Query 1 / SELECT FROM users WHERE id = 1; COMMIT; / lock-based REPEATABLE READ /

Isolation level Dirty reads Non-repeatable reads Phantoms Read Uncommitted may occur may occur(1) may occur(2) Read Committed don't occur may occur(1) may occur(2) Repeatable Read don't occur don't occur may occur(2) Serializable don't occur don't occur don't occur

(1): 返回修改后的值 (2): 会返回新插入的值 Serializable : 均会阻塞事务2的更新, 直到事务1提交



* mysql的锁
* spring的事务传播级别, 实际上就是多个方法调用, 发生多个事务交叉时, 如何选择当一个事务提交或者回滚后对另一个事务的影响
* mybatis, 如果mapper不用interface, 用class可以注入吗
*