主頁 > 後端開發 > MyBatis攔截器

MyBatis攔截器

2021-06-17 06:24:20 後端開發

一、攔截物件和介面實作示例

        MyBatis攔截器的作用是在于Dao到DB中間進行額外的處理,大部分情況下通過mybatis的xml配置sql都可以達到想要的DB操作效果,然而存在一些類似或者相同的查詢條件或者查詢要求,這些可以通過攔截器的實作可以提升開發效率,比如:分頁、插入和更新時間/人、資料權限、SQL監控日志等,

  • Mybatis支持四種物件攔截Executor、StatementHandler、PameterHandler和ResultSetHandler

  1. Executor:攔截執行器的方法,

  2. StatementHandler:攔截Sql語法構建的處理,

  3. ParameterHandler:攔截引數的處理,

  4. ResultHandler:攔截結果集的處理,

 1 public interface Executor {
 2     ResultHandler NO_RESULT_HANDLER = null;
 3     int update(MappedStatement var1, Object var2) throws SQLException;
 4     <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
 5     <E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
 6     <E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
 7     List<BatchResult> flushStatements() throws SQLException;
 8     void commit(boolean var1) throws SQLException;
 9     void rollback(boolean var1) throws SQLException;
10     CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
11     boolean isCached(MappedStatement var1, CacheKey var2);
12     void clearLocalCache();
13     void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
14     Transaction getTransaction();
15     void close(boolean var1);
16     boolean isClosed();
17     void setExecutorWrapper(Executor var1);
18 }
19 public interface StatementHandler {
20     Statement prepare(Connection var1, Integer var2) throws SQLException;
21     void parameterize(Statement var1) throws SQLException;
22     void batch(Statement var1) throws SQLException;
23     int update(Statement var1) throws SQLException;
24     <E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;
25     <E> Cursor<E> queryCursor(Statement var1) throws SQLException;
26     BoundSql getBoundSql();
27     ParameterHandler getParameterHandler();
28 }
29 public interface ParameterHandler {
30     Object getParameterObject();
31     void setParameters(PreparedStatement var1) throws SQLException;
32 }
33 public interface ResultHandler<T> {
34     void handleResult(ResultContext<? extends T> var1);
35 }

     攔截的執行順序是Executor->StatementHandler->ParameterHandler->ResultHandler

  • MyBatis提供的攔截器介面:

1 public interface Interceptor {
2     Object intercept(Invocation var1) throws Throwable;
3     default Object plugin(Object target) {
4         return Plugin.wrap(target, this);
5     }
6     default void setProperties(Properties properties) {}
7 }

  Object intercept方法用于攔截器的實作;

  Object plugin方法用于判斷執行攔截器的型別;

  void setProperties方法用于獲取配置項的屬性,

  • 攔截物件和攔截器介面的結合,自定義的攔截器類需要實作攔截器介面,并通過注解@Intercepts和引數@Signature來宣告要攔截的物件,

        @Signature引數type是攔截物件,method是攔截的方法,即上面的四個類對應的方法,args是攔截方法對應的引數(方法存在多載因此需要指明引數個數和型別)

         @Intercepts可以有多個@Signature,即一個攔截器實作類可以同時攔截多個物件及方法,示例如下:

    1. Executor->intercept

    2. StatementHandler->intercept

    3. ParameterHandler->intercept

    4. ResultHandler->intercept

 1 @Intercepts({
 2         @Signature(
 3                 type = Executor.class,
 4                 method = "query",
 5                 args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
 6         )
 7 })
 8 public class SelectPlugin implements Interceptor {
 9     @Override
10     public Object intercept(Invocation invocation) throws Throwable {
11         if (invocation.getTarget() instanceof Executor) {
12             System.out.println("SelectPlugin");
13         }
14         return invocation.proceed();
15     }
16     @Override
17     public Object plugin(Object target) {
18         if (target instanceof Executor) {
19             return Plugin.wrap(target, this);
20         }
21         return target;
22     }
23     @Override
24     public void setProperties(Properties properties) {}
25 }
26 @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
27 public class StatementPlugin implements Interceptor {
28     @Override
29     public Object intercept(Invocation invocation) throws Throwable {
30         if (invocation.getTarget() instanceof StatementHandler) {
31             System.out.println("StatementPlugin");
32         }
33         return invocation.proceed();
34     }
35     @Override
36     public Object plugin(Object target) {
37         if (target instanceof StatementHandler) {
38             return Plugin.wrap(target, this);
39         }
40         return target;
41     }
42     @Override
43     public void setProperties(Properties properties) {}
44 }
45 @Intercepts({@Signature(type = ParameterHandler.class,method = "setParameters",args = {PreparedStatement.class})})
46 public class ParameterPlugin implements Interceptor {
47     @Override
48     public Object intercept(Invocation invocation) throws Throwable {
49         if (invocation.getTarget() instanceof ParameterHandler) {
50             System.out.println("ParameterPlugin");
51         }
52         return invocation.proceed();
53     }
54     @Override
55     public Object plugin(Object target) {
56         if (target instanceof ParameterHandler) {
57             return Plugin.wrap(target, this);
58         }
59         return target;
60     }
61     @Override
62     public void setProperties(Properties properties) {}
63 }
64 @Intercepts({@Signature(type = ResultHandler.class,method = "handleResult",args = {ResultContext.class})})
65 public class ResultPlugin implements Interceptor {
66     @Override
67     public Object intercept(Invocation invocation) throws Throwable {
68         if (invocation.getTarget() instanceof ResultHandler) {
69             System.out.println("ResultPlugin");
70         }
71         return invocation.proceed();
72     }
73     @Override
74     public Object plugin(Object target) {
75         if (target instanceof ResultHandler) {
76             return Plugin.wrap(target, this);
77         }
78         return target;
79     }
80     @Override
81     public void setProperties(Properties properties) {}
82 }

二、攔截器注冊的三種方式

        前面介紹了Mybatis的攔截物件及其介面的實作方式,那么在專案中如何注冊攔截器呢?本文中給出三種注冊方式,

        1.XML注冊

        xml注冊是最基本的方式,是通過在Mybatis組態檔中plugins元素來進行注冊的,一個plugin對應著一個攔截器,在plugin元素可以指定property子元素,在注冊定義攔截器時把對應攔截器的所有property通過Interceptor的setProperties方法注入給攔截器,因此攔截器注冊xml方式如下:

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration
 3   PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4   "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5 <configuration>
 6     <!-- ...... -->
 7     <plugins>
 8        <plugin interceptor="com.tiantian.mybatis.interceptor.MyInterceptor">
 9            <property name="prop1" value="prop1"/>
10            <property name="prop2" value="prop2"/>
11        </plugin>
12     </plugins>
13     <!-- ...... -->
14 </configuration>

        2.配置類注冊

       配置類注冊是指通過Mybatis的配置類中宣告注冊攔截器,配置類注冊也可以通過Properties類給Interceptor的setProperties方法注入引數,具體參考如下:

 1 @Configuration
 2 public class MyBatisConfig {
 3     @Bean
 4     public String MyBatisInterceptor(SqlSessionFactory sqlSessionFactory) {
 5         UpdatePlugin executorInterceptor = new UpdatePlugin();
 6         Properties properties = new Properties();
 7         properties.setProperty("prop1", "value1");
 8         // 給攔截器添加自定義引數
 9         executorInterceptor.setProperties(properties);
10         sqlSessionFactory.getConfiguration().addInterceptor(executorInterceptor);
11         sqlSessionFactory.getConfiguration().addInterceptor(new StatementPlugin());
12         sqlSessionFactory.getConfiguration().addInterceptor(new ResultPlugin());
13         sqlSessionFactory.getConfiguration().addInterceptor(new ParameterPlugin());
14         // sqlSessionFactory.getConfiguration().addInterceptor(new SelectPlugin());
15         return "interceptor";
16     }
17 
18     // 與sqlSessionFactory.getConfiguration().addInterceptor(new SelectPlugin());效果一致
19     @Bean
20     public SelectPlugin SelectInterceptor() {
21         SelectPlugin interceptor = new SelectPlugin();
22         Properties properties = new Properties();
23         // 呼叫properties.setProperty方法給攔截器設定自定義引數
24         interceptor.setProperties(properties);
25         return interceptor;
26     }
27 }

        3.注解方式

          通過@Component注解方式是最簡單的方式,在不需要轉遞自定義引數時可以使用,方便快捷,

@Component
@Intercepts({
        @Signature(
                type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
        )
})
public class SelectPlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof Executor) {
            System.out.println("SelectPlugin");
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

三、ParameterHandler引數改寫-修改時間和修改人統一插入

        針對具體的攔截器實作進行描述,日常編碼需求中會碰到修改時需要插入修改的時間和人員,如果要用xml的方式去寫非常麻煩,而通過攔截器的方式可以快速實作全域的插入修改時間和人員,先看代碼:

 1 @Component
 2 @Intercepts({
 3         @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),
 4 })
 5 public class MyBatisInterceptor implements Interceptor {
 6     @Override
 7     public Object intercept(Invocation invocation) throws Throwable {
 8         // 引數代理
 9         if (invocation.getTarget() instanceof ParameterHandler) {
10             System.out.println("ParameterHandler");
11             // 自動添加操作員資訊
12             autoAddOperatorInfo(invocation);
13         }
14         return invocation.proceed();
15     }
16 
17     @Override
18     public Object plugin(Object target) {
19         return Plugin.wrap(target, this);
20     }
21 
22     @Override
23     public void setProperties(Properties properties) {
24 
25     }
26 
27     /**
28      * 自動添加操作員資訊
29      *
30      * @param invocation 代理物件
31      * @throws Throwable 例外
32      */
33     private void autoAddOperatorInfo(Invocation invocation) throws Throwable {
34         System.out.println("autoInsertCreatorInfo");
35         // 獲取代理的引數物件ParameterHandler
36         ParameterHandler ph = (ParameterHandler) invocation.getTarget();
37         // 通過MetaObject獲取ParameterHandler的反射內容
38         MetaObject metaObject = MetaObject.forObject(ph,
39                 SystemMetaObject.DEFAULT_OBJECT_FACTORY,
40                 SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
41                 new DefaultReflectorFactory());
42         // 通過MetaObject反射的內容獲取MappedStatement
43         MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement");
44         // 當sql型別為INSERT或UPDATE時,自動插入操作員資訊
45         if (mappedStatement.getSqlCommandType() == SqlCommandType.INSERT ||
46                 mappedStatement.getSqlCommandType() == SqlCommandType.UPDATE) {
47             // 獲取引數物件
48             Object obj = ph.getParameterObject();
49             if (null != obj) {
50                 // 通過反射獲取引數物件的屬性
51                 Field[] fields = obj.getClass().getDeclaredFields();
52                 // 遍歷引數物件的屬性
53                 for (Field f : fields) {
54                     // 如果sql是INSERT,且存在createdAt屬性
55                     if ("createdAt".equals(f.getName()) && mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) {
56                         // 設定允許訪問反射屬性
57                         f.setAccessible(true);
58                         // 如果沒有設定createdAt屬性,則自動為createdAt屬性添加當前的時間
59                         if (null == f.get(obj)) {
60                             // 設定createdAt屬性為當前時間
61                             f.set(obj, LocalDateTime.now());
62                         }
63                     }
64                     // 如果sql是INSERT,且存在createdBy屬性
65                     if ("createdBy".equals(f.getName()) && mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) {
66                         // 設定允許訪問反射屬性
67                         f.setAccessible(true);
68                         // 如果沒有設定createdBy屬性,則自動為createdBy屬性添加當前登錄的人員
69                         if (null == f.get(obj)) {
70                             // 設定createdBy屬性為當前登錄的人員
71                             f.set(obj, 0);
72                         }
73                     }
74                     // sql為INSERT或UPDATE時均需要設定updatedAt屬性
75                     if ("updatedAt".equals(f.getName())) {
76                         f.setAccessible(true);
77                         if (null == f.get(obj)) {
78                             f.set(obj, LocalDateTime.now());
79                         }
80                     }
81                     // sql為INSERT或UPDATE時均需要設定updatedBy屬性
82                     if ("updatedBy".equals(f.getName())) {
83                         f.setAccessible(true);
84                         if (null == f.get(obj)) {
85                             f.set(obj, 0);
86                         }
87                     }
88                 }
89 
90                 // 通過反射獲取ParameterHandler的parameterObject屬性
91                 Field parameterObject = ph.getClass().getDeclaredField("parameterObject");
92                 // 設定允許訪問parameterObject屬性
93                 parameterObject.setAccessible(true);
94                 // 將上面設定的新引數物件設定到ParameterHandler的parameterObject屬性
95                 parameterObject.set(ph, obj);
96             }
97         }
98     }
99 }

        攔截器的介面實作參考前文,這里著重介紹autoAddOperatorInfo方法里的相關類,

        1.ParameterHandler

        介面原始碼:

1 public interface ParameterHandler {
2     Object getParameterObject();
3     void setParameters(PreparedStatement var1) throws SQLException;
4 }

        提供兩個方法:

        getParameterObject是獲取引數物件,可能存在null,需要注意null指標,

        setParameters是控制如何設定SQL引數,即sql陳述句中配置的java物件和jdbc型別對應的關系,例如#{id,jdbcType=INTEGER},id默認型別是javaType=class java.lang.Integer,

        該介面有一個默認的實作類,原始碼如下:

 1 public class DefaultParameterHandler implements ParameterHandler {
 2     private final TypeHandlerRegistry typeHandlerRegistry;
 3     private final MappedStatement mappedStatement;
 4     private final Object parameterObject;
 5     private final BoundSql boundSql;
 6     private final Configuration configuration;
 7 
 8     public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
 9         this.mappedStatement = mappedStatement;
10         this.configuration = mappedStatement.getConfiguration();
11         this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
12         this.parameterObject = parameterObject;
13         this.boundSql = boundSql;
14     }
15 
16     public Object getParameterObject() {
17         return this.parameterObject;
18     }
19 
20     public void setParameters(PreparedStatement ps) {
21         ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
22         List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
23         if (parameterMappings != null) {
24             for(int i = 0; i < parameterMappings.size(); ++i) {
25                 ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
26                 if (parameterMapping.getMode() != ParameterMode.OUT) {
27                     String propertyName = parameterMapping.getProperty();
28                     Object value;
29                     if (this.boundSql.hasAdditionalParameter(propertyName)) {
30                         value = https://www.cnblogs.com/guohaixin/archive/2021/06/16/this.boundSql.getAdditionalParameter(propertyName);
31                     } else if (this.parameterObject == null) {
32                         value = https://www.cnblogs.com/guohaixin/archive/2021/06/16/null;
33                     } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
34                         value = https://www.cnblogs.com/guohaixin/archive/2021/06/16/this.parameterObject;
35                     } else {
36                         MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
37                         value =https://www.cnblogs.com/guohaixin/archive/2021/06/16/ metaObject.getValue(propertyName);
38                     }
39 
40                     TypeHandler typeHandler = parameterMapping.getTypeHandler();
41                     JdbcType jdbcType = parameterMapping.getJdbcType();
42                     if (value =https://www.cnblogs.com/guohaixin/archive/2021/06/16/= null && jdbcType == null) {
43                         jdbcType = this.configuration.getJdbcTypeForNull();
44                     }
45 
46                     try {
47                         typeHandler.setParameter(ps, i + 1, value, jdbcType);
48                     } catch (SQLException | TypeException var10) {
49                         throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
50                     }
51                 }
52             }
53         }
54 
55     }
56 }

        通過DefaultParameterHandler實作類我們知道通過ParameterHandler可以獲取到哪些屬性和方法,其中包括我們下面一個重要的類MappedStatement,

        2.MappedStatement

        MyBatis的mapper檔案中的每個select/update/insert/delete標簽會被決議器決議成一個對應的MappedStatement物件,也就是一個MappedStatement物件描述一條SQL陳述句,MappedStatement物件屬性如下:

 1     // mapper組態檔名
 2     private String resource;
 3     // mybatis的全域資訊,如jdbc
 4     private Configuration configuration;
 5     // 節點的id屬性加命名空間,如:com.example.mybatis.dao.UserMapper.selectByExample
 6     private String id;
 7     private Integer fetchSize;
 8     private Integer timeout;
 9     private StatementType statementType;
10     private ResultSetType resultSetType;
11     private SqlSource sqlSource;
12     private Cache cache;
13     private ParameterMap parameterMap;
14     private List<ResultMap> resultMaps;
15     private boolean flushCacheRequired;
16     private boolean useCache;
17     private boolean resultOrdered;
18     // sql陳述句的型別:select、update、delete、insert
19     private SqlCommandType sqlCommandType;
20     private KeyGenerator keyGenerator;
21     private String[] keyProperties;
22     private String[] keyColumns;
23     private boolean hasNestedResultMaps;
24     private String databaseId;
25     private Log statementLog;
26     private LanguageDriver lang;
27     private String[] resultSets;

        在本例中通過MappedStatement物件的sqlCommandType來判斷當前的sql型別是insert、update來進行下一步的操作,

四、通過StatementHandler改寫SQL

        StatementHandler是用于封裝JDBC Statement操作,負責對JDBC Statement的操作,如設定引數,并將Statement結果集轉換成List集合,

        實作代碼如下:

        洗掉注解標記

@Target({ElementType.METHOD})  //表示注解的使用范圍
@Retention(RetentionPolicy.RUNTIME) //注解的保存時間
@Documented    //檔案顯示
public @interface DeletedAt {
    boolean has() default true;
}

        Dao層添加洗掉注解,為false時不添加洗掉標志

1 @Mapper
2 public interface AdminProjectDao {
3     @DeletedAt(has = false)
4     List<AdminProjectPo> selectProjects(AdminProjectPo po);
5 }

        攔截器通過洗掉注解標記判斷是否添加洗掉標志

 1 @Component
 2 @Intercepts({
 3         @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
 4 })
 5 public class MyBatisInterceptor implements Interceptor {
 6     @Override
 7     public Object intercept(Invocation invocation) throws Throwable {
 8         if (invocation.getTarget() instanceof StatementHandler) {
 9             System.out.println("StatementHandler");
10             checkHasDeletedAtField(invocation);
11         }
12         return invocation.proceed();
13     }
14 
15     @Override
16     public Object plugin(Object target) {
17         return Plugin.wrap(target, this);
18     }
19 
20     @Override
21     public void setProperties(Properties properties) {
22 
23     }
24 
25     /**
26      * 檢查查詢是否需要添加洗掉標志欄位
27      *
28      * @param invocation 代理物件
29      * @throws Throwable 例外
30      */
31     private void checkHasDeletedAtField(Invocation invocation) throws Throwable {
32         System.out.println("checkHasDeletedAtField");
33         StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
34         // 通過MetaObject訪問物件的屬性
35         MetaObject metaObject = MetaObject.forObject(
36                 statementHandler,
37                 SystemMetaObject.DEFAULT_OBJECT_FACTORY,
38                 SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
39                 new DefaultReflectorFactory());
40         // 獲取成員變數mappedStatement
41         MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
42         // 如果sql型別是查詢
43         if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT) {
44             // 獲取洗掉注解標志
45             DeletedAt annotation = null;
46             String id = mappedStatement.getId();
47             String className = id.substring(0, id.lastIndexOf("."));
48             String methodName = id.substring(id.lastIndexOf(".") + 1);
49             Class<?> aClass = Class.forName(className);
50             Method[] declaredMethods = aClass.getDeclaredMethods();
51             for (Method declaredMethod : declaredMethods) {
52                 declaredMethod.setAccessible(true);
53                 //方法名相同,并且注解是DeletedAt
54                 if (methodName.equals(declaredMethod.getName()) && declaredMethod.isAnnotationPresent(DeletedAt.class)) {
55                     annotation = declaredMethod.getAnnotation(DeletedAt.class);
56                 }
57             }
58             // 如果注解不存在或者注解為true(默認為true) 則為mysql陳述句增加洗掉標志
59             if (annotation == null || annotation.has()) {
60                 BoundSql boundSql = statementHandler.getBoundSql();
61                 //獲取到原始sql陳述句
62                 String sql = boundSql.getSql();
63                 //通過反射修改sql陳述句
64                 Field field = boundSql.getClass().getDeclaredField("sql");
65                 field.setAccessible(true);
66                 String newSql = sql.replaceAll("9=9", "9=9 and deleted_at is null ");
67                 field.set(boundSql, newSql);
68             }
69         }
70     }
71 }

        在SQL陳述句替換上需要能識別到要被替換的內容,因此在xml的sql陳述句中加入特殊標志"9=9",該標志不影響原來SQL的執行結果,不同的過濾條件可以設定不同的標志,是一個比較巧妙的替換方式,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/287995.html

標籤:其他

上一篇:性能分析之CPU分析-從CPU呼叫高到具體代碼行(JAVA)

下一篇:Python 區域變數和全域變數 - Python零基礎入門教程

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more