最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis mapper動(dòng)態(tài)代理的原理解析

 更新時(shí)間:2019年08月21日 08:29:32   作者:全me村的希望  
這篇文章主要介紹了Mybatis mapper動(dòng)態(tài)代理的原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言

在開始動(dòng)態(tài)代理的原理講解以前,我們先看一下集成mybatis以后dao層不使用動(dòng)態(tài)代理以及使用動(dòng)態(tài)代理的兩種實(shí)現(xiàn)方式,通過對(duì)比我們自己實(shí)現(xiàn)dao層接口以及mybatis動(dòng)態(tài)代理可以更加直觀的展現(xiàn)出mybatis動(dòng)態(tài)代理替我們所做的工作,有利于我們理解動(dòng)態(tài)代理的過程,講解完以后我們?cè)龠M(jìn)行動(dòng)態(tài)代理的原理解析,此講解基于mybatis的環(huán)境已經(jīng)搭建完成,并且已經(jīng)實(shí)現(xiàn)了基本的用戶類編寫以及用戶類的Dao接口的聲明,下面是Dao層的接口代碼

public interface UserDao {
 /*
 查詢所有用戶信息
  */
 List<User> findAll();
 /**
  * 保存用戶
  * @param user
  */
 void save(User user);

 /**
  * 更新用戶
  * @return
  */
 void update(User user);
 /**
  * 刪除用戶
  */
 void delete(Integer userId);
 /**
  * 查找一個(gè)用戶
  * @param userId
  * @return
  */
 User findOne(Integer userId);
 /**
  * 根據(jù)名字模糊查詢
  * @param name
  * @return
  */
 List<User> findByName(String name);
 /**
  * 根據(jù)組合對(duì)象進(jìn)行模糊查詢
  * @param vo
  * @return
  */
 List<User> findByQueryVo(QueryVo vo);
}

一、Mybatis dao層兩種實(shí)現(xiàn)方式的對(duì)比

1.dao層不使用動(dòng)態(tài)代理

dao層不使用動(dòng)態(tài)代理的話,就需要我們自己實(shí)現(xiàn)dao層的接口,為了簡(jiǎn)便起見,我只是實(shí)現(xiàn)了Dao接口中的findAll方法,以此方法為例子來(lái)展現(xiàn)我們自己實(shí)現(xiàn)Dao的方式的情況,讓我們來(lái)看代碼:

public class UserDaoImpl implements UserDao{
 private SqlSessionFactory factory;
 public UserDaoImpl(SqlSessionFactory factory){
  this.factory = factory;
 }
 public List<User> findAll() {
  //1.獲取sqlSession對(duì)象
  SqlSession sqlSession = factory.openSession();
  //2.調(diào)用selectList方法
  List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll");
  //3.關(guān)閉流
  sqlSession.close();
  return list;
 }
 public void save(User user) {
 }
 public void update(User user) {
 }
 public void delete(Integer userId) {
 }
 public User findOne(Integer userId) {
  return null;
 }
 public List<User> findByName(String name) {
  return null;
 }
 public List<User> findByQueryVo(QueryVo vo) {
  return null;
 }

這里的關(guān)鍵代碼 List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll"),需要我們自己手動(dòng)調(diào)用SqlSession里面的方法,基于動(dòng)態(tài)代理的方式最后的目標(biāo)也是成功的調(diào)用到這里。

注意:如果是添加,更新或者刪除操作的話需要在方法中增加事務(wù)的提交。

2.dao層使用Mybatis的動(dòng)態(tài)代理

使用動(dòng)態(tài)代理的話Dao層的接口聲明完成以后只需要在使用的時(shí)候通過SqlSession對(duì)象的getMapper方法獲取對(duì)應(yīng)Dao接口的代理對(duì)象,關(guān)鍵代碼如下:

//3.獲取SqlSession對(duì)象
SqlSession session = factory.openSession();
//4.獲取dao的代理對(duì)象
UserDao mapper = session.getMapper(UserDao.class);
//5.執(zhí)行查詢所有的方法
List<User> list = mapper.findAll();

獲取到dao層的代理對(duì)象以后通過代理對(duì)象調(diào)用查詢方法就可以實(shí)現(xiàn)查詢所有用戶列表的功能。

二、Mybatis動(dòng)態(tài)代理實(shí)現(xiàn)方式的原理解析

動(dòng)態(tài)代理中最重要的類:SqlSession、MapperProxy、MapperMethod,下面開始從入口方法到調(diào)用結(jié)束的過程分析。

1.調(diào)用方法的開始:

//4.獲取dao的代理對(duì)象

UserDao mapper = session.getMapper(UserDao.class); 因?yàn)镾qlSesseion為接口,所以我們通過Debug方式發(fā)現(xiàn)這里使用的實(shí)現(xiàn)類為DefaultSqlSession。

2.找到DeaultSqlSession中的getMapper方法,發(fā)現(xiàn)這里沒有做其他的動(dòng)作,只是將工作繼續(xù)拋到了Configuration類中,Configuration為類不是接口,可以直接進(jìn)入該類的getMapper方法中

@Override
 public <T> T getMapper(Class<T> type) {
 return configuration.<T>getMapper(type, this);
 }

3. 找到Configuration類的getMapper方法,這里也是將工作繼續(xù)交到MapperRegistry的getMapper的方法中,所以我們繼續(xù)向下進(jìn)行。

 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 return mapperRegistry.getMapper(type, sqlSession);
 }

4. 找到MapperRegistry的getMapper的方法,看到這里發(fā)現(xiàn)和以前不一樣了,通過MapperProxyFactory的命名方式我們知道這里將通過這個(gè)工廠生成我們所關(guān)注的MapperProxy的代理類,然后我們通過mapperProxyFactory.newInstance(sqlSession);進(jìn)入MapperProxyFactory的newInstance方法中

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
 if (mapperProxyFactory == null) {
  throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 }
 try {
  return mapperProxyFactory.newInstance(sqlSession);
 } catch (Exception e) {
  throw new BindingException("Error getting mapper instance. Cause: " + e, e);
 }
 }

5. 找到MapperProxyFactory的newIntance方法,通過參數(shù)類型SqlSession可以得知,上面的調(diào)用先進(jìn)入第二個(gè)newInstance方法中并創(chuàng)建我們所需要重點(diǎn)關(guān)注的MapperProxy對(duì)象,第二個(gè)方法中再調(diào)用第一個(gè)newInstance方法并將MapperProxy對(duì)象傳入進(jìn)去,根據(jù)該對(duì)象創(chuàng)建代理類并返回。這里已經(jīng)得到需要的代理類了,但是我們的代理類所做的工作還得繼續(xù)向下看MapperProxy類。

protected T newInstance(MapperProxy<T> mapperProxy) {
 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
 }
 public T newInstance(SqlSession sqlSession) {
 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
 return newInstance(mapperProxy);
 }

6. 找到MapperProxy類,發(fā)現(xiàn)其確實(shí)實(shí)現(xiàn)了JDK動(dòng)態(tài)代理必須實(shí)現(xiàn)的接口InvocationHandler,所以我們重點(diǎn)關(guān)注invoke()方法,這里看到在invoke方法里先獲取MapperMethod類,然后調(diào)用mapperMethod.execute(),所以我們繼續(xù)查看MapperMethod類的execute方法。

public class MapperProxy<T> implements InvocationHandler, Serializable {
 private static final long serialVersionUID = -6424540398559729838L;
 private final SqlSession sqlSession;
 private final Class<T> mapperInterface;
 private final Map<Method, MapperMethod> methodCache;
 public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
 this.sqlSession = sqlSession;
 this.mapperInterface = mapperInterface;
 this.methodCache = methodCache;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 try {
  if (Object.class.equals(method.getDeclaringClass())) {
  return method.invoke(this, args);
  } else if (isDefaultMethod(method)) {
  return invokeDefaultMethod(proxy, method, args);
  }
 } catch (Throwable t) {
  throw ExceptionUtil.unwrapThrowable(t);
 }
 final MapperMethod mapperMethod = cachedMapperMethod(method);
 return mapperMethod.execute(sqlSession, args);
 }

 private MapperMethod cachedMapperMethod(Method method) {
 MapperMethod mapperMethod = methodCache.get(method);
 if (mapperMethod == null) {
  mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
  methodCache.put(method, mapperMethod);
 }
 return mapperMethod;
 }

 @UsesJava7
 private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
  throws Throwable {
 final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
  .getDeclaredConstructor(Class.class, int.class);
 if (!constructor.isAccessible()) {
  constructor.setAccessible(true);
 }
 final Class<?> declaringClass = method.getDeclaringClass();
 return constructor
  .newInstance(declaringClass,
   MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
    | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
  .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
 }
 /**
 * Backport of java.lang.reflect.Method#isDefault()
 */
 private boolean isDefaultMethod(Method method) {
 return ((method.getModifiers()
  & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
  && method.getDeclaringClass().isInterface();
 }
}

7. 找到類MapperMethod類的execute方法,發(fā)現(xiàn)execute中通過調(diào)用本類中的其他方法獲取并封裝返回結(jié)果,我們來(lái)看一下MapperMethod整個(gè)類。

public Object execute(SqlSession sqlSession, Object[] args) {
 Object result;
 switch (command.getType()) {
  case INSERT: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.insert(command.getName(), param));
  break;
  }
  case UPDATE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.update(command.getName(), param));
  break;
  }
  case DELETE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.delete(command.getName(), param));
  break;
  }
  case SELECT:
  if (method.returnsVoid() && method.hasResultHandler()) {
   executeWithResultHandler(sqlSession, args);
   result = null;
  } else if (method.returnsMany()) {
   result = executeForMany(sqlSession, args);
  } else if (method.returnsMap()) {
   result = executeForMap(sqlSession, args);
  } else if (method.returnsCursor()) {
   result = executeForCursor(sqlSession, args);
  } else {
   Object param = method.convertArgsToSqlCommandParam(args);
   result = sqlSession.selectOne(command.getName(), param);
  }
  break;
  case FLUSH:
  result = sqlSession.flushStatements();
  break;
  default:
  throw new BindingException("Unknown execution method for: " + command.getName());
 }
 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
  throw new BindingException("Mapper method '" + command.getName() 
   + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
 }
 return result;
 }

8. MapperMethod類是整個(gè)代理機(jī)制的核心類,對(duì)SqlSession中的操作進(jìn)行了封裝使用。

該類里有兩個(gè)內(nèi)部類SqlCommand和MethodSignature。 SqlCommand用來(lái)封裝CRUD操作,也就是我們?cè)趚ml中配置的操作的節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)都會(huì)生成一個(gè)MappedStatement類。

MethodSignature用來(lái)封裝方法的參數(shù)以及返回類型,在execute的方法中我們發(fā)現(xiàn)在這里又回到了SqlSession中的接口調(diào)用,和我們自己實(shí)現(xiàn)UerDao接口的方式中直接用SqlSession對(duì)象調(diào)用DefaultSqlSession的實(shí)現(xiàn)類的方法是一樣的,經(jīng)過一大圈的代理又回到了原地,這就是整個(gè)動(dòng)態(tài)代理的實(shí)現(xiàn)過程了。

public class MapperMethod {
 private final SqlCommand command;
 private final MethodSignature method;
 public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
 this.command = new SqlCommand(config, mapperInterface, method);
 this.method = new MethodSignature(config, mapperInterface, method);
 }
 public Object execute(SqlSession sqlSession, Object[] args) {
 Object result;
 switch (command.getType()) {
  case INSERT: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.insert(command.getName(), param));
  break;
  }
  case UPDATE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.update(command.getName(), param));
  break;
  }
  case DELETE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.delete(command.getName(), param));
  break;
  }
  case SELECT:
  if (method.returnsVoid() && method.hasResultHandler()) {
   executeWithResultHandler(sqlSession, args);
   result = null;
  } else if (method.returnsMany()) {
   result = executeForMany(sqlSession, args);
  } else if (method.returnsMap()) {
   result = executeForMap(sqlSession, args);
  } else if (method.returnsCursor()) {
   result = executeForCursor(sqlSession, args);
  } else {
   Object param = method.convertArgsToSqlCommandParam(args);
   result = sqlSession.selectOne(command.getName(), param);
  }
  break;
  case FLUSH:
  result = sqlSession.flushStatements();
  break;
  default:
  throw new BindingException("Unknown execution method for: " + command.getName());
 }
 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
  throw new BindingException("Mapper method '" + command.getName() 
   + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
 }
 return result;
 }

 private Object rowCountResult(int rowCount) {
 final Object result;
 if (method.returnsVoid()) {
  result = null;
 } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
  result = rowCount;
 } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
  result = (long)rowCount;
 } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
  result = rowCount > 0;
 } else {
  throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
 }
 return result;
 }

 private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
 MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
 if (void.class.equals(ms.getResultMaps().get(0).getType())) {
  throw new BindingException("method " + command.getName() 
   + " needs either a @ResultMap annotation, a @ResultType annotation," 
   + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
 }
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
 } else {
  sqlSession.select(command.getName(), param, method.extractResultHandler(args));
 }
 }

 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
 List<E> result;
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
 } else {
  result = sqlSession.<E>selectList(command.getName(), param);
 }
 // issue #510 Collections & arrays support
 if (!method.getReturnType().isAssignableFrom(result.getClass())) {
  if (method.getReturnType().isArray()) {
  return convertToArray(result);
  } else {
  return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
  }
 }
 return result;
 }

 private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
 Cursor<T> result;
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);
 } else {
  result = sqlSession.<T>selectCursor(command.getName(), param);
 }
 return result;
 }

 private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
 Object collection = config.getObjectFactory().create(method.getReturnType());
 MetaObject metaObject = config.newMetaObject(collection);
 metaObject.addAll(list);
 return collection;
 }
 @SuppressWarnings("unchecked")
 private <E> Object convertToArray(List<E> list) {
 Class<?> arrayComponentType = method.getReturnType().getComponentType();
 Object array = Array.newInstance(arrayComponentType, list.size());
 if (arrayComponentType.isPrimitive()) {
  for (int i = 0; i < list.size(); i++) {
  Array.set(array, i, list.get(i));
  }
  return array;
 } else {
  return list.toArray((E[])array);
 }
 }
 private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
 Map<K, V> result;
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
 } else {
  result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
 }
 return result;
 }
 public static class ParamMap<V> extends HashMap<String, V> {
 private static final long serialVersionUID = -2212268410512043556L;
 @Override
 public V get(Object key) {
  if (!super.containsKey(key)) {
  throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
  }
  return super.get(key);
 }
 }
 public static class SqlCommand {
 private final String name;
 private final SqlCommandType type;

 public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
  final String methodName = method.getName();
  final Class<?> declaringClass = method.getDeclaringClass();
  MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
   configuration);
  if (ms == null) {
  if (method.getAnnotation(Flush.class) != null) {
   name = null;
   type = SqlCommandType.FLUSH;
  } else {
   throw new BindingException("Invalid bound statement (not found): "
    + mapperInterface.getName() + "." + methodName);
  }
  } else {
  name = ms.getId();
  type = ms.getSqlCommandType();
  if (type == SqlCommandType.UNKNOWN) {
   throw new BindingException("Unknown execution method for: " + name);
  }
  }
 }
 public String getName() {
  return name;
 }
 public SqlCommandType getType() {
  return type;
 }
 private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
  Class<?> declaringClass, Configuration configuration) {
  String statementId = mapperInterface.getName() + "." + methodName;
  if (configuration.hasStatement(statementId)) {
  return configuration.getMappedStatement(statementId);
  } else if (mapperInterface.equals(declaringClass)) {
  return null;
  }
  for (Class<?> superInterface : mapperInterface.getInterfaces()) {
  if (declaringClass.isAssignableFrom(superInterface)) {
   MappedStatement ms = resolveMappedStatement(superInterface, methodName,
    declaringClass, configuration);
   if (ms != null) {
   return ms;
   }
  }
  }
  return null;
 }
 }

 public static class MethodSignature {
 private final boolean returnsMany;
 private final boolean returnsMap;
 private final boolean returnsVoid;
 private final boolean returnsCursor;
 private final Class<?> returnType;
 private final String mapKey;
 private final Integer resultHandlerIndex;
 private final Integer rowBoundsIndex;
 private final ParamNameResolver paramNameResolver;

 public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
  Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
  if (resolvedReturnType instanceof Class<?>) {
  this.returnType = (Class<?>) resolvedReturnType;
  } else if (resolvedReturnType instanceof ParameterizedType) {
  this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
  } else {
  this.returnType = method.getReturnType();
  }
  this.returnsVoid = void.class.equals(this.returnType);
  this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
  this.returnsCursor = Cursor.class.equals(this.returnType);
  this.mapKey = getMapKey(method);
  this.returnsMap = (this.mapKey != null);
  this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
  this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
  this.paramNameResolver = new ParamNameResolver(configuration, method);
 }

 public Object convertArgsToSqlCommandParam(Object[] args) {
  return paramNameResolver.getNamedParams(args);
 }

 public boolean hasRowBounds() {
  return rowBoundsIndex != null;
 }

 public RowBounds extractRowBounds(Object[] args) {
  return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
 }

 public boolean hasResultHandler() {
  return resultHandlerIndex != null;
 }

 public ResultHandler extractResultHandler(Object[] args) {
  return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
 }

 public String getMapKey() {
  return mapKey;
 }

 public Class<?> getReturnType() {
  return returnType;
 }

 public boolean returnsMany() {
  return returnsMany;
 }

 public boolean returnsMap() {
  return returnsMap;
 }

 public boolean returnsVoid() {
  return returnsVoid;
 }

 public boolean returnsCursor() {
  return returnsCursor;
 }
 private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
  Integer index = null;
  final Class<?>[] argTypes = method.getParameterTypes();
  for (int i = 0; i < argTypes.length; i++) {
  if (paramType.isAssignableFrom(argTypes[i])) {
   if (index == null) {
   index = i;
   } else {
   throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
   }
  }
  }
  return index;
 }
 private String getMapKey(Method method) {
  String mapKey = null;
  if (Map.class.isAssignableFrom(method.getReturnType())) {
  final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
  if (mapKeyAnnotation != null) {
   mapKey = mapKeyAnnotation.value();
  }
  }
  return mapKey;
 }
 }

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

  • Linux下MySQL數(shù)據(jù)庫(kù)的主從同步復(fù)制配置

    Linux下MySQL數(shù)據(jù)庫(kù)的主從同步復(fù)制配置

    這篇文章主要介紹了Linux下MySQL數(shù)據(jù)庫(kù)的主從同步配置,
    2017-11-11
  • 詳解MySQL存儲(chǔ)過程參數(shù)有三種類型(in、out、inout)

    詳解MySQL存儲(chǔ)過程參數(shù)有三種類型(in、out、inout)

    MySQL 存儲(chǔ)過程參數(shù)有三種類型:in、out、inout。它們各有什么作用和特點(diǎn)呢
    2012-07-07
  • MySql 5.6.14 Win32位免安裝解壓縮版配置教程

    MySql 5.6.14 Win32位免安裝解壓縮版配置教程

    本文給大家介紹mysql 5.6.14 win32 位免安裝解壓縮版配置方法,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,對(duì)mysql5.6.14 免安裝解壓縮版配置方法感興趣的朋友一起看看吧
    2016-11-11
  • MySQL聯(lián)合索引與最左匹配原則的實(shí)現(xiàn)

    MySQL聯(lián)合索引與最左匹配原則的實(shí)現(xiàn)

    最左匹配原則在我們MySQL開發(fā)過程中和面試過程中經(jīng)常遇到,為了加深印象和理解,我在這里把MySQL的最左匹配原則詳細(xì)的講解一下,感興趣的可以了解一下
    2023-12-12
  • 計(jì)算機(jī)二級(jí)考試MySQL知識(shí)點(diǎn) mysql alter命令

    計(jì)算機(jī)二級(jí)考試MySQL知識(shí)點(diǎn) mysql alter命令

    這篇文章主要為大家詳細(xì)介紹了計(jì)算機(jī)二級(jí)考試MySQL知識(shí)點(diǎn),詳細(xì)介紹了mysql中alter命令的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Mysql根據(jù)某層部門ID查詢所有下級(jí)多層子部門的示例

    Mysql根據(jù)某層部門ID查詢所有下級(jí)多層子部門的示例

    這篇文章主要介紹了Mysql根據(jù)某層部門ID查詢所有下級(jí)多層子部門的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • mysql建庫(kù)時(shí)提示Specified key was too long max key length is 1000 bytes的問題的解決方法

    mysql建庫(kù)時(shí)提示Specified key was too long max key length is 1000

    本文將詳細(xì)提供mysql建庫(kù)時(shí)提示Specified key was too long max key length is 1000 bytes的問題的解決方法,有需求的朋友可以參考
    2012-11-11
  • Ubuntu上mysql的安裝及使用(通用版)

    Ubuntu上mysql的安裝及使用(通用版)

    今天小編就為大家分享一篇關(guān)于Ubuntu上mysql的安裝及使用(通用版),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • Mysql中InnoDB與MyISAM索引差異詳解(最新整理)

    Mysql中InnoDB與MyISAM索引差異詳解(最新整理)

    InnoDB和MyISAM在索引實(shí)現(xiàn)和特性上有差異,包括聚集索引、非聚集索引、事務(wù)支持、并發(fā)控制、覆蓋索引、主鍵約束、外鍵支持和物理存儲(chǔ)結(jié)構(gòu)等方面,InnoDB更適合事務(wù)型應(yīng)用,而MyISAM適合只讀或讀多寫少的場(chǎng)景,本文介紹Mysql中InnoDB與MyISAM索引差異,感興趣的朋友一起看看吧
    2025-03-03
  • 最新評(píng)論

    五台县| 开江县| 西平县| 泸定县| 凌源市| 山东| 梧州市| 屯门区| 浪卡子县| 大关县| 读书| 金坛市| 石渠县| 织金县| 鄂托克前旗| 宁都县| 留坝县| 昌都县| 泽州县| 于都县| 阜阳市| 南乐县| 德安县| 苍山县| 宁阳县| 颍上县| 泰来县| 湘潭市| 杭锦后旗| 灵璧县| 永年县| 广河县| 湖州市| 岳阳县| 故城县| 南江县| 徐州市| 扎赉特旗| 南宫市| 大渡口区| 扶绥县|