欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入了解MyBatis參數(shù)

 更新時(shí)間:2018年12月25日 11:51:30   作者:isea533  
今天小編就為大家分享一篇關(guān)于深入了解MyBatis參數(shù),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧

深入了解MyBatis參數(shù)

相信很多人可能都遇到過下面這些異常:

  • "Parameter 'xxx' not found. Available parameters are [...]"
  • "Could not get property 'xxx' from xxxClass. Cause:
  • "The expression 'xxx' evaluated to a null value."
  • "Error evaluating expression 'xxx'. Return value (xxxxx) was not iterable."

不只是上面提到的這幾個(gè),我認(rèn)為有很多的錯誤都產(chǎn)生在和參數(shù)有關(guān)的地方。

想要避免參數(shù)引起的錯誤,我們需要深入了解參數(shù)。

想了解參數(shù),我們首先看MyBatis處理參數(shù)和使用參數(shù)的全部過程。

本篇由于為了便于理解和深入,使用了大量的源碼,因此篇幅較長,需要一定的耐心看完,本文一定會對你起到很大的幫助。

參數(shù)處理過程

處理接口形式的入?yún)?/strong>

在使用MyBatis時(shí),有兩種使用方法。一種是使用的接口形式,另一種是通過SqlSession調(diào)用命名空間。這兩種方式在傳遞參數(shù)時(shí)是不一樣的,命名空間的方式更直接,但是多個(gè)參數(shù)時(shí)需要我們自己創(chuàng)建Map作為入?yún)?。相比而言,使用接口形式更簡單?/p>

接口形式的參數(shù)是由MyBatis自己處理的。如果使用接口調(diào)用,入?yún)⑿枰?jīng)過額外的步驟處理入?yún)?,之后就和命名空間方式一樣了。

在MapperMethod.java會首先經(jīng)過下面方法來轉(zhuǎn)換參數(shù):

public Object convertArgsToSqlCommandParam(Object[] args) {
 final int paramCount = params.size();
 if (args == null || paramCount == 0) {
  return null;
 } else if (!hasNamedParameters && paramCount == 1) {
  return args[params.keySet().iterator().next()];
 } else {
  final Map<String, Object> param = new ParamMap<Object>();
  int i = 0;
  for (Map.Entry<Integer, String> entry : params.entrySet()) {
   param.put(entry.getValue(), args[entry.getKey()]);
   // issue #71, add param names as param1, param2...but ensure backward compatibility
   final String genericParamName = "param" + String.valueOf(i + 1);
   if (!param.containsKey(genericParamName)) {
    param.put(genericParamName, args[entry.getKey()]);
   }
   i++;
  }
  return param;
 }
}

在這里有個(gè)很關(guān)鍵的params,這個(gè)參數(shù)類型為Map<Integer, String>,他會根據(jù)接口方法按順序記錄下接口參數(shù)的定義的名字,如果使用@Param指定了名字,就會記錄這個(gè)名字,如果沒有記錄,那么就會使用它的序號作為名字。

例如有如下接口:

List<User> select(@Param('sex')String sex,Integer age);

那么他對應(yīng)的params如下:

{
  0:'sex',
  1:'1'
}

繼續(xù)看上面的convertArgsToSqlCommandParam方法,這里簡要說明3種情況:

  • 入?yún)閚ull或沒有時(shí),參數(shù)轉(zhuǎn)換為null
  • 沒有使用@Param注解并且只有一個(gè)參數(shù)時(shí),返回這一個(gè)參數(shù)
  • 使用了@Param注解或有多個(gè)參數(shù)時(shí),將參數(shù)轉(zhuǎn)換為Map1類型,并且還根據(jù)參數(shù)順序存儲了key為param1,param2的參數(shù)。

注意:從第3種情況來看,建議各位有多個(gè)入?yún)⒌臅r(shí)候通過@Param指定參數(shù)名,方便后面(動態(tài)sql)的使用。

經(jīng)過上面方法的處理后,在MapperMethod中會繼續(xù)往下調(diào)用命名空間方式的方法:

Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.<E>selectList(command.getName(), param);

從這之后開始按照統(tǒng)一的方式繼續(xù)處理入?yún)ⅰ?/p>

處理集合

不管是selectOne還是selectMap方法,歸根結(jié)底都是通過selectList進(jìn)行查詢的,不管是delete還是insert方法,都是通過update方法操作的。在selectList和update中所有參數(shù)的都進(jìn)行了統(tǒng)一的處理。

在DefaultSqlSession.java中的wrapCollection方法:

private Object wrapCollection(final Object object) {
 if (object instanceof Collection) {
  StrictMap<Object> map = new StrictMap<Object>();
  map.put("collection", object);
  if (object instanceof List) {
   map.put("list", object);
  }
  return map;   
 } else if (object != null && object.getClass().isArray()) {
  StrictMap<Object> map = new StrictMap<Object>();
  map.put("array", object);
  return map;
 }
 return object;
}

這里特別需要注意的一個(gè)地方是map.put("collection", object),這個(gè)設(shè)計(jì)是為了支持Set類型,需要等到MyBatis 3.3.0版本才能使用。

wrapCollection處理的是只有一個(gè)參數(shù)時(shí),集合和數(shù)組的類型轉(zhuǎn)換成Map2類型,并且有默認(rèn)的Key,從這里你能大概看到為什么<foreach>中默認(rèn)情況下寫的array和list(Map類型沒有默認(rèn)值map)。

參數(shù)的使用

參數(shù)的使用分為兩部分:

  • 第一種就是常見#{username}或者${username}。
  • 第二種就是在動態(tài)SQL中作為條件,例如<if test="username!=null and username !=''">。

下面對這兩種進(jìn)行詳細(xì)講解,為了方便理解,先講解第二種情況。

在動態(tài)SQL條件中使用參數(shù)

關(guān)于動態(tài)SQL的基礎(chǔ)內(nèi)容可以查看官方文檔。

動態(tài)SQL為什么會處理參數(shù)呢?

主要是因?yàn)閯討B(tài)SQL中的<if>,<bind>,<foreache>都會用到表達(dá)式,表達(dá)式中會用到屬性名,屬性名對應(yīng)的屬性值如何獲取呢?獲取方式就在這關(guān)鍵的一步。不知道多少人遇到Could not get property xxx from xxxClass或: Parameter ‘xxx' not found. Available parameters are[…],都是不懂這里引起的。

在DynamicContext.java中,從構(gòu)造方法看起:

public DynamicContext(Configuration configuration, Object parameterObject) {
 if (parameterObject != null && !(parameterObject instanceof Map)) {
  MetaObject metaObject = configuration.newMetaObject(parameterObject);
  bindings = new ContextMap(metaObject);
 } else {
  bindings = new ContextMap(null);
 }
 bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
 bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
}

這里的Object parameterObject就是我們經(jīng)過前面兩步處理后的參數(shù)。這個(gè)參數(shù)經(jīng)過前面兩步處理后,到這里的時(shí)候,他只有下面三種情況:

  • null,如果沒有入?yún)⒒蛘呷雲(yún)⑹莕ull,到這里也是null。
  • Map類型,除了null之外,前面兩步主要是封裝成Map類型。
  • 數(shù)組、集合和Map以外的Object類型,可以是基本類型或者實(shí)體類。

看上面構(gòu)造方法,如果參數(shù)是1,2情況時(shí),執(zhí)行代碼bindings = new ContextMap(null);參數(shù)是3情況時(shí)執(zhí)行if中的代碼。我們看看ContextMap類,這是一個(gè)內(nèi)部靜態(tài)類,代碼如下:

static class ContextMap extends HashMap<String, Object> {
 private MetaObject parameterMetaObject;
 public ContextMap(MetaObject parameterMetaObject) {
  this.parameterMetaObject = parameterMetaObject;
 }
 public Object get(Object key) {
  String strKey = (String) key;
  if (super.containsKey(strKey)) {
   return super.get(strKey);
  }
  if (parameterMetaObject != null) {
   // issue #61 do not modify the context when reading
   return parameterMetaObject.getValue(strKey);
  }
  return null;
 }
}

我們先繼續(xù)看DynamicContext的構(gòu)造方法,在if/else之后還有兩行:

bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());

其中兩個(gè)Key分別為:

public static final String PARAMETER_OBJECT_KEY = "_parameter";
public static final String DATABASE_ID_KEY = "_databaseId";

也就是說1,2兩種情況的時(shí)候,參數(shù)值只存在于"_parameter"的鍵值中。3情況的時(shí)候,參數(shù)值存在于"_parameter"的鍵值中,也存在于bindings本身。

當(dāng)動態(tài)SQL取值的時(shí)候會通過OGNL從bindings中獲取值。MyBatis在OGNL中注冊了ContextMap:

static {
 OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
}

當(dāng)從ContextMap取值的時(shí)候,會執(zhí)行ContextAccessor中的如下方法:

@Override
public Object getProperty(Map context, Object target, Object name)
  throws OgnlException {
 Map map = (Map) target;
 Object result = map.get(name);
 if (map.containsKey(name) || result != null) {
  return result;
 }
 Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
 if (parameterObject instanceof Map) {
  return ((Map)parameterObject).get(name);
 }
 return null;
}

參數(shù)中的target就是ContextMap類型的,所以可以直接強(qiáng)轉(zhuǎn)為Map類型。

參數(shù)中的name就是我們寫在動態(tài)SQL中的屬性名。

下面舉例說明這三種情況:

null的時(shí)候:

不管name是什么(name="_databaseId"除外,可能會有值),此時(shí)Object result = map.get(name);得到的result=null。
在Object parameterObject = map.get(PARAMETER_OBJECT_KEY);中parameterObject=null,因此最后返回的結(jié)果是null。
在這種情況下,不管寫什么樣的屬性,值都會是null,并且不管屬性是否存在,都不會出錯。

Map類型:

此時(shí)Object result = map.get(name);一般也不會有值,因?yàn)閰?shù)值只存在于"_parameter"的鍵值中。
然后到Object parameterObject = map.get(PARAMETER_OBJECT_KEY);,此時(shí)獲取到我們的參數(shù)值。
在從參數(shù)值((Map)parameterObject).get(name)根據(jù)name來獲取屬性值。
在這一步的時(shí)候,如果name屬性不存在,就會報(bào)錯:

throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());

name屬性是什么呢,有什么可選值呢?這就是處理接口形式的入?yún)⒑吞幚砑咸幚砗笏鶕碛械腒ey。
如果你遇到過類似異常,相信看到這兒就明白原因了。

數(shù)組、集合和Map以外的Object類型:

這種類型經(jīng)過了下面的處理:

MetaObject metaObject = configuration.newMetaObject(parameterObject);
bindings = new ContextMap(metaObject);

MetaObject是MyBatis的一個(gè)反射類,可以很方便的通過getValue方法獲取對象的各種屬性(支持集合數(shù)組和Map,可以多級屬性點(diǎn).訪問,如user.username,user.roles[1].rolename)。 現(xiàn)在分析這種情況。

首先通過name獲取屬性時(shí)Object result = map.get(name);,根據(jù)上面ContextMap類中的get方法:

public Object get(Object key) {
String strKey = (String) key;
if (super.containsKey(strKey)) {
 return super.get(strKey);
}
if (parameterMetaObject != null) {
 return parameterMetaObject.getValue(strKey);
}
return null;
}

可以看到這里會優(yōu)先從Map中取該屬性的值,如果不存在,那么一定會執(zhí)行到下面這行代碼:

return parameterMetaObject.getValue(strKey)

如果name剛好是對象的一個(gè)屬性值,那么通過MetaObject反射可以獲取該屬性值。如果該對象不包含name屬性的值,就會報(bào)錯:

throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);

理解這三種情況后,使用動態(tài)SQL應(yīng)該不會有參數(shù)名方面的問題了。

在SQL語句中使用參數(shù)

SQL中的兩種形式#{username}或者${username},雖然看著差不多,但是實(shí)際處理過程差別很大,而且很容易出現(xiàn)莫名其妙的錯誤。

${username}的使用方式為OGNL方式獲取值,和上面的動態(tài)SQL一樣,這里先說這種情況。

${propertyName}參數(shù)

在TextSqlNode.java中有一個(gè)內(nèi)部的靜態(tài)類BindingTokenParser,現(xiàn)在只看其中的handleToken方法:

@Override
public String handleToken(String content) {
 Object parameter = context.getBindings().get("_parameter");
 if (parameter == null) {
  context.getBindings().put("value", null);
 } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
  context.getBindings().put("value", parameter);
 }
 Object value = OgnlCache.getValue(content, context.getBindings());
 String srtValue = (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"
 checkInjection(srtValue);
 return srtValue;
}

從put("value"這個(gè)地方可以看出來,MyBatis會創(chuàng)建一個(gè)默認(rèn)為"value"的值,也就是說,在xml中的SQL中可以直接使用${value},從else if可以看出來,只有是簡單類型的時(shí)候,才會有值。

關(guān)于這點(diǎn),舉個(gè)簡單例子,如果接口為List<User> selectOrderby(String column),如果xml內(nèi)容為:

<select id="selectOrderby" resultType="User">
select * from user order by ${value}
</select>

這種情況下,雖然沒有指定一個(gè)value屬性,但是MyBatis會自動把參數(shù)column賦值進(jìn)去。

再往下的代碼:

Object value = OgnlCache.getValue(content, context.getBindings());
String srtValue = (value == null ? "" : String.valueOf(value));

這里和動態(tài)SQL就一樣了,通過OGNL方式來獲取值。

看到這里使用OGNL這種方式時(shí),你有沒有別的想法?

特殊用法:你是否在SQL查詢中使用過某些固定的碼值?一旦碼值改變的時(shí)候需要改動很多地方,但是你又不想把碼值作為參數(shù)傳進(jìn)來,怎么解決呢?你可能已經(jīng)明白了。

就是通過OGNL的方式,例如有如下一個(gè)碼值類:

package com.abel533.mybatis;
public interface Code{
  public static final String ENABLE = "1";
  public static final String DISABLE = "0";
}

如果在xml,可以這么使用:

<select id="selectUser" resultType="User">
  select * from user where enable = ${@com.abel533.mybatis.Code@ENABLE}
</select>

除了碼值之外,你可以使用OGNL支持的各種方法,如調(diào)用靜態(tài)方法。

#{propertyName}參數(shù)

這種方式比較簡單,復(fù)雜屬性的時(shí)候使用的MyBatis的MetaObject。

在DefaultParameterHandler.java中:

public void setParameters(PreparedStatement ps) throws SQLException {
 ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 if (parameterMappings != null) {
  for (int i = 0; i < parameterMappings.size(); i++) {
   ParameterMapping parameterMapping = parameterMappings.get(i);
   if (parameterMapping.getMode() != ParameterMode.OUT) {
    Object value;
    String propertyName = parameterMapping.getProperty();
    if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
     value = boundSql.getAdditionalParameter(propertyName);
    } else if (parameterObject == null) {
     value = null;
    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
     value = parameterObject;
    } else {
     MetaObject metaObject = configuration.newMetaObject(parameterObject);
     value = metaObject.getValue(propertyName);
    }
    TypeHandler typeHandler = parameterMapping.getTypeHandler();
    JdbcType jdbcType = parameterMapping.getJdbcType();
    if (value == null && jdbcType == null) {
     jdbcType = configuration.getJdbcTypeForNull();
    }
    typeHandler.setParameter(ps, i + 1, value, jdbcType);
   }
  }
 }
}

上面這段代碼就是從參數(shù)中取#{propertyName}值的方法,這段代碼的主要邏輯就是if/else判斷的地方,單獨(dú)拿出來分析:

if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
 value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
 value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
 value = parameterObject;
} else {
 MetaObject metaObject = configuration.newMetaObject(parameterObject);
 value = metaObject.getValue(propertyName);
}
  • 首先看第一個(gè)if,當(dāng)使用<foreach>的時(shí)候,MyBatis會自動生成額外的動態(tài)參數(shù),如果propertyName是動態(tài)參數(shù),就會從動態(tài)參數(shù)中取值。
  • 第二個(gè)if,如果參數(shù)是null,不管屬性名是什么,都會返回null。
  • 第三個(gè)if,如果參數(shù)是一個(gè)簡單類型,或者是一個(gè)注冊了typeHandler的對象類型,就會直接使用該參數(shù)作為返回值,和屬性名無關(guān)。
  • 最后一個(gè)else,這種情況下是復(fù)雜對象或者M(jìn)ap類型,通過反射方便的取值。

下面我們說明上面四種情況下的參數(shù)名注意事項(xiàng)。

動態(tài)參數(shù),這里的參數(shù)名和值都由MyBatis動態(tài)生成的,因此我們沒法直接接觸,也不需要管這兒的命名。但是我們可以了解一下這兒的命名規(guī)則,當(dāng)以后錯誤信息看到的時(shí)候,我們可以確定出錯的地方。
在ForEachSqlNode.java中:

private static String itemizeItem(String item, int i) {
return new StringBuilder(ITEM_PREFIX).append(item).append("_").append(i).toString();
}

其中ITEM_PRFIX為public static final String ITEM_PREFIX = "__frch_";。
如果在<foreach>中的collection="userList" item="user",那么對userList循環(huán)產(chǎn)生的動態(tài)參數(shù)名就是:

__frch_user_0,__frch_user_1,__frch_user_2…

如果訪問動態(tài)參數(shù)的屬性,如user.username會被處理成__frch_user_0.username,這種參數(shù)值的處理過程在更早之前解析SQL的時(shí)候就已經(jīng)獲取了對應(yīng)的參數(shù)值。具體內(nèi)容看下面有關(guān)<foreach>的詳細(xì)內(nèi)容。

參數(shù)為null,由于這里的判斷和參數(shù)名無關(guān),因此入?yún)ull的時(shí)候,在xml中寫的#{name}不管name寫什么,都不會出錯,值都是null。

可以直接使用typeHandler處理的類型。最常見的就是基本類型,例如有這樣一個(gè)接口方法User selectById(@Param("id")Integer id),在xml中使用id的時(shí)候,我們可以隨便使用屬性名,不管用什么樣的屬性名,值都是id。

復(fù)雜對象或者M(jìn)ap類型一般都是我們需要注意的地方,這種情況下,就必須保證入?yún)@些屬性,如果沒有就會報(bào)錯。這一點(diǎn)和可以參考上面有關(guān)MetaObject的地方。

<foreach>詳解

所有動態(tài)SQL類型中,<foreach>似乎是遇到問題最多的一個(gè)。

例如有下面的方法:

<insert id="insertUserList">
 INSERT INTO user(username,password)
 VALUES
 <foreach collection="userList" item="user" separator=",">
  (#{user.username},#{user.password})
 </foreach>
</insert>

對應(yīng)的接口:

int insertUserList(@Param("userList")List<User> list);

我們通過foreach源碼,看看MyBatis如何處理上面這個(gè)例子。

在ForEachSqlNode.java中的apply方法中的前兩行:

Map<String, Object> bindings = context.getBindings();
final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings);

這里的bindings參數(shù)熟悉嗎?上面提到過很多。經(jīng)過一系列的參數(shù)處理后,這兒的bindings如下:

{
 "_parameter":{
  "param1":list,
  "userList":list
 },
 "_databaseId":null,
}

collectionExpression就是collection="userList"的值userList。

我們看看evaluator.evaluateIterable如何處理這個(gè)參數(shù),在ExpressionEvaluator.java中的evaluateIterable方法:

public Iterable<?> evaluateIterable(String expression, Object parameterObject) {
  Object value = OgnlCache.getValue(expression, parameterObject);
  if (value == null) {
   throw new BuilderException("The expression '" + expression + "' evaluated to a null value.");
  }
  if (value instanceof Iterable) {
   return (Iterable<?>) value;
  }
  if (value.getClass().isArray()) {
    int size = Array.getLength(value);
    List<Object> answer = new ArrayList<Object>();
    for (int i = 0; i < size; i++) {
      Object o = Array.get(value, i);
      answer.add(o);
    }
    return answer;
  }
  if (value instanceof Map) {
   return ((Map) value).entrySet();
  }
  throw new BuilderException("Error evaluating expression '" + expression + "'. Return value (" + value + ") was not iterable.");
}

首先通過看第一行代碼:

Object value = OgnlCache.getValue(expression, parameterObject);

這里通過OGNL獲取到了userList的值。獲取userList值的時(shí)候可能出現(xiàn)異常,具體可以參考上面動態(tài)SQL部分的內(nèi)容。

userList的值分四種情況。

  • value == null,這種情況直接拋出異常BuilderException。
  • value instanceof Iterable,實(shí)現(xiàn)Iterable接口的直接返回,如Collection的所有子類,通常是List。
  • value.getClass().isArray()數(shù)組的情況,這種情況會轉(zhuǎn)換為List返回。
  • value instanceof Map如果是Map,通過((Map) value).entrySet()返回一個(gè)Set類型的參數(shù)。

通過上面處理后,返回的值,是一個(gè)Iterable類型的值,這個(gè)值可以使用for (Object o : iterable)這種形式循環(huán)。

在ForEachSqlNode中對iterable循環(huán)的時(shí)候,有一段需要關(guān)注的代碼:

if (o instanceof Map.Entry) {
  @SuppressWarnings("unchecked") 
  Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;
  applyIndex(context, mapEntry.getKey(), uniqueNumber);
  applyItem(context, mapEntry.getValue(), uniqueNumber);
} else {
  applyIndex(context, i, uniqueNumber);
  applyItem(context, o, uniqueNumber);
}

如果是通過((Map) value).entrySet()返回的Set,那么循環(huán)取得的子元素都是Map.Entry類型,這個(gè)時(shí)候會將mapEntry.getKey()存儲到index中,mapEntry.getValue()存儲到item中。

如果是List,那么會將序號i存到index中,mapEntry.getValue()存儲到item中。

<foreach>常見錯誤補(bǔ)充

當(dāng)collection="userList"的值userList中的User是一個(gè)繼承自Map的類型時(shí),你需要保證<foreach>循環(huán)中用到的所有對象的屬性必須存在,Map類型存在的問題通常是,如果某個(gè)值是null,一般是不存在相應(yīng)的key,這種情況會導(dǎo)致<foreach>出錯,會報(bào)找不到__frch_user_x參數(shù)。所以這種情況下,就是值是null,你也需要map.put(key,null)。

最后

這篇文章很長,寫這篇文章耗費(fèi)的時(shí)間也很長,超過10小時(shí),寫到半夜兩點(diǎn)都沒寫完。

這篇文章真的非常有用,如果你對Mybatis有一定的了解,這篇文章幾乎是必讀的一篇。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接

相關(guān)文章

  • Java編程偽共享與緩存行填充

    Java編程偽共享與緩存行填充

    這篇文章主要介紹了Java編程偽共享與緩存行填充,下面文章Disruptor提到的CPU緩存話題,做了一些嘗試和研究,如Disruptor所說,CPU有緩存?zhèn)喂蚕淼膯栴},并且通過緩存行填充能完美的解決這個(gè)問題,需要的朋友可以參考一下
    2021-09-09
  • rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊(duì)列持久化(durable)及消息持久化

    rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊(duì)列持久化(durable)及消息持久化

    這篇文章主要介紹了rabbitmq學(xué)習(xí)系列教程之消息應(yīng)答(autoAck)、隊(duì)列持久化(durable)及消息持久化,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • 詳解java中繼承關(guān)系類加載順序問題

    詳解java中繼承關(guān)系類加載順序問題

    這篇文章主要介紹了詳解java中繼承關(guān)系類加載順序問題的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • 解決poi導(dǎo)出時(shí)單元格樣式被覆蓋問題

    解決poi導(dǎo)出時(shí)單元格樣式被覆蓋問題

    這篇文章主要介紹了解決poi導(dǎo)出時(shí)單元格樣式被覆蓋問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java注解之Repeatable解讀

    Java注解之Repeatable解讀

    這篇文章主要介紹了Java注解之Repeatable,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • java中redissonClient 分布式鎖的使用

    java中redissonClient 分布式鎖的使用

    在集群的情況下,用戶多次請求接口時(shí),存入的內(nèi)容可能會導(dǎo)致重復(fù),這時(shí)候就可以使用分布式鎖來限制,本文就來介紹一下java中redissonClient 分布式鎖的使用,感興趣的可以了解一下
    2024-03-03
  • java使用Feign實(shí)現(xiàn)聲明式Restful風(fēng)格調(diào)用

    java使用Feign實(shí)現(xiàn)聲明式Restful風(fēng)格調(diào)用

    這篇文章主要為大家詳細(xì)介紹了java使用Feign實(shí)現(xiàn)聲明式Restful風(fēng)格調(diào)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • Netty網(wǎng)絡(luò)編程零基礎(chǔ)入門

    Netty網(wǎng)絡(luò)編程零基礎(chǔ)入門

    Netty是一個(gè)異步的、基于事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用框架,用于快速開發(fā)可維護(hù)、高性能的網(wǎng)絡(luò)服務(wù)器和客戶端,如果你還不了解它的使用,就趕快繼續(xù)往下看吧
    2022-08-08
  • JPA設(shè)置默認(rèn)字段及其長度詳解

    JPA設(shè)置默認(rèn)字段及其長度詳解

    JPA是Java Persistence API的簡稱,中文名Java持久層API,是JDK 5.0注解或XML描述對象-關(guān)系表的映射關(guān)系,并將運(yùn)行期的實(shí)體對象持久化到數(shù)據(jù)庫中。本文主要介紹了JPA如何設(shè)置默認(rèn)字段及其長度,感興趣的同學(xué)可以了解一下
    2021-12-12
  • SpringBoot如何從配置文件中讀取配置參數(shù)

    SpringBoot如何從配置文件中讀取配置參數(shù)

    這篇文章主要介紹了SpringBoot如何從配置文件中讀取配置參數(shù)問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01

最新評論