一、日志概述
衡量软件产品的质量时,是否具备完善的日志是个非常重要的因素。开发测试阶段,需要日志帮助我们完善功能和发现bug;生产上,当出现生产问题时,又需要日志帮助我们定位问题发生现场的情况。同时,日志还是开发与运维之间的桥梁,有助于运维管理人员快速查找系统的故障和瓶颈,良好的日志在一个软件中占了非常重要的地位。
日志对于排查问题很有帮助,但不是越多越好,过多冗余的日志,不管是日志输出还是保存日志到文件,都会消耗服务器的资源,我们希望某些日志在开发测试阶段打出来,在版本稳定投产之后只打印关键日志,还有想根据日志的不同类型分门别类归纳到日志文件中,日志文件达到一定大小或保存一定时间就删除,这些日志需求的场景都是很常见的,在 Java 的世界中已经有多种成熟开源的日志框架,常用的有 Log4j
、Log4j2
、Apache Commons Log
、java.util.logging
、slf4j
等,它们的框架 API 略有差异,不过使用上整体大同小异。
MyBatis 聚合了上述多种优秀的日志框架,提供框架内部输出详细日志的能力,并且为了屏蔽不同日志框架 API 的差异,提供了一个统一的接口,并且基于该接口定制了针对不同日志框架的适配器,使得用户可以根据自身喜好和使用习惯方便地选择要使用的日志框架。
MyBatis 的日志模块位于 org.apache.ibatis.logging
包中,如下所示:
二、整体设计
日志模块的整体设计如下:
日志模块使用了适配器模式,对外提供的统一日志接口是 Log
,每种日志框架都有对应的适配器来适配接口的定义,LogFactory
是日志工厂类,MyBatis 中最重要的日志就是执行 SQL 的时候打印调试日志,为了适应格式化日志的要求,定义了 BaseJdbcLogger
及其几个子类,通过动态代理的方式,在执行 JDBC 相关方法时拦截,并输出响应的日志,常见的有 SQL 语句、传入参数、执行影响行数等。
三、Log
不同的日志框架对日志级别的定义略有差异,比如 Log4j
的日志级别有:trace、debug、info、warn、error、fatal;java.util.logging
的日志级别有:ALL、FINEST、FINER、FINE、CONFIG、INFO、WARNING、SEVERE、OFF。MyBatis 统一提供了 trace、debug、warn、error 四个级别,这基本与主流日志框架的日志级别类似,可以满足绝大多数场景的日志需求。
org.apache.ibatis.logging.Log
定义了日志模块的功能,所有的日志适配器都要实现该接口,接口定义如下:
public interface Log {
boolean isDebugEnabled();
boolean isTraceEnabled();
void error(String s, Throwable e);
void error(String s);
void debug(String s);
void trace(String s);
void warn(String s);
}
下面以 Log4jImpl
和 Jdk14LoggingImpl
为例说明日志框架适配器的实现,其他适配器类似。
1、Log4jImpl
Log4jImpl
是 Log4j 日志框架对接口的适配器,其源码如下:
public class Log4jImpl implements Log {
// 显示调用者位置的参数
private static final String FQCN = Log4jImpl.class.getName();
private Logger log;
public Log4jImpl(String clazz) {
log = Logger.getLogger(clazz);
}
@Override
public boolean isDebugEnabled() {
return log.isDebugEnabled();
}
@Override
public boolean isTraceEnabled() {
return log.isTraceEnabled();
}
@Override
public void error(String s, Throwable e) {
log.log(FQCN, Level.ERROR, s, e);
}
@Override
public void error(String s) {
log.log(FQCN, Level.ERROR, s, null);
}
@Override
public void debug(String s) {
log.log(FQCN, Level.DEBUG, s, null);
}
@Override
public void trace(String s) {
log.log(FQCN, Level.TRACE, s, null);
}
@Override
public void warn(String s) {
log.log(FQCN, Level.WARN, s, null);
}
}
这里有一个特别的地方,FQCN
参数,该参数传入的 #log()
方法的第一个参数传入,在 Log4j 中参数名叫 callFQCN
,log4j 把传递进来的 callerFQCN 在调用堆栈中一一比较,相等后,再往上一层即认为是用户的调用类。
2、Jdk14LoggingImpl
Jdk14LoggingImpl
是 JDK 内置日志框架对接口的适配器,其源码如下:
public class Jdk14LoggingImpl implements Log {
private Logger log;
public Jdk14LoggingImpl(String clazz) {
log = Logger.getLogger(clazz);
}
@Override
public boolean isDebugEnabled() {
return log.isLoggable(Level.FINE);
}
@Override
public boolean isTraceEnabled() {
return log.isLoggable(Level.FINER);
}
@Override
public void error(String s, Throwable e) {
log.log(Level.SEVERE, s, e);
}
@Override
public void error(String s) {
log.log(Level.SEVERE, s);
}
@Override
public void debug(String s) {
log.log(Level.FINE, s);
}
@Override
public void trace(String s) {
log.log(Level.FINER, s);
}
@Override
public void warn(String s) {
log.log(Level.WARNING, s);
}
}
四、LogFactory
public static final String MARKER = "MYBATIS";
// 记录当前使用的第三方日志组件所对应的适配器的构造方法
private static Constructor logConstructor;
// 针对每种日志组件调用 tryImplementation() 方法进行尝试加载,具体调用顺序是:
// useSlf4jLogging() --> useCommonsLogging() --> useLog4J2Logging() -->
// useLog4JLogging() --> useJdkLogging() --> useNoLogging()
static {
tryImplementation(new Runnable() {
@Override
public void run() {
useSlf4jLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useCommonsLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useLog4J2Logging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useLog4JLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useJdkLogging();
}
});
tryImplementation(new Runnable() {
@Override
public void run() {
useNoLogging();
}
});
}
// 工厂类对外只提供静态方法,构造函数私有
private LogFactory() {
// disable construction
}
LogFactory
中定义了 logConstructor
成员来保存当前使用的第三方日志所对应的适配器的构造方法,第一次加载工厂类时,会调用静态代码块初始化尝试加载各种日志组件,优先级从代码中可以看出,最先加载的适配器会赋值给 logConstructor
,代码如下:
private static void tryImplementation(Runnable runnable) {
// 先判断是否已加载到适配器,是则放弃加载其他适配器
if (logConstructor == null) {
try {
runnable.run();
} catch (Throwable t) {
// ignore
}
}
}
虽然将方法调用包装成了一个 runnable,但这里注意不是用 new Thread(runnable).start()
去启动一个新线程,只是简单的调用 runnable.run()
所以对各个适配器的加载是串行的,优先级靠前的适配器加载不到才会真正尝试去加载后面的适配器,各个加载适配器的方法里,都会去调用 #setImplementation()
完成加载动作,传参是适配器对应的 Class 对象,以 Log4j 为例:
public static synchronized void useLog4JLogging() {
setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
}
private static void setImplementation(Class implClass) {
try {
// 获取构造参数为 String 的构造器
Constructor candidate = implClass.getConstructor(String.class);
// 用构造器实例化 Log 对象
Log log = candidate.newInstance(LogFactory.class.getName());
if (log.isDebugEnabled()) {
log.debug("Logging initialized using '" + implClass + "' adapter.");
}
// 赋值
logConstructor = candidate;
} catch (Throwable t) {
throw new LogException("Error setting Log implementation. Cause: " + t, t);
}
}
这里的 #useLog4JLogging()
方法定义用了 synchronized
关键字,这里有点疑惑,如果是加载各个适配器都启动一个新线程还可以理解,但问题是,并没有启新线程,所以我猜测这里跟初始化加载无关,跟调用线程有关,因为 logConstructor
必须是线程安全的,因为暴露给外界的 #getLog
方法用了该变量去创建具体的 Log 对象,如下:
public static Log getLog(Class aClass) {
return getLog(aClass.getName());
}
public static Log getLog(String logger) {
try {
return logConstructor.newInstance(logger);
} catch (Throwable t) {
throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);
}
}
五、JDBC 调试
业务代码在打印日志时,往往是先创建一个 Logger
,然后在一些关键的地方,比如接口调用、错误异常处理等地方打上日志,但有些时候,出于保持代码清晰或其他原因,在某些地方不想掺杂日志打印的代码,这时候就可以使用动态代理,动态代理常用的有 JDK 动态代理、CGLIB 等,MyBatis 打印 SQL 执行日志方便 JDBC 调试的功能,就是基于 JDK 动态代理实现的。
动态代理,一般无非是在某个方法调用的前后添加某些处理,对于使用者来说,只要拿到代理对象,就可以将它当成普通对象来使用,代理的逻辑对使用者来说是透明。
1、JDK 动态代理的使用
使用 JDK 动态代理,要满足几个条件:
(1)定义一个接口。
(2)被代理的类要实现(1)中接口。
(3)定义一个实现了 InvocationHandler
接口的类,该类中会封装被代理的对象,并且该类实例会被传递给 Proxy.newProxyInstance
方法创建代理对象。
下面举例说明:
1.1 定义接口
public interface HelloWorld {
public void sayHelloWorld();
}
1.2 代理类
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHelloWorld() {
System.out.println("Hello World");
}
}
1.3 InvocationHandler 实现类
/**
* 使用JDK实现动态代理,要求被代理的对象必须实现一个接口
*/
public class JdkProxyExample implements InvocationHandler {
// 真实对象
private Object target = null;
// 绑定真实对象并返回代理对象
public Object bind(Object target) {
this.target = target;
// 第三个参数类型是InvocationHandler,代理对象被调用时,会先执行Handler接口的invoke方法
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
* 代理方法逻辑
* @param proxy 代理对象
* @param method 当前调度方法
* @param args 当前方法参数
* @return 代理结果返回
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑方法");
System.out.println("在调度真实对象之前的服务");
Object obj = method.invoke(target, args);
System.out.println("在调度真实对象之后的服务");
return obj;
}
public static void main(String[] args) {
JdkProxyExample jdk = new JdkProxyExample();
HelloWorld proxy = (HelloWorld) jdk.bind(new HelloWorldImpl());
proxy.sayHelloWorld();
}
}
执行结果如下:
进入代理逻辑方法
在调度真实对象之前的服务
Hello World
在调度真实对象之后的服务
2、BaseJdbcLogger 及其子类
BaseJdbcLogger
及其子类通过使用动态代理,实现了执行 SQL 操作时,无感输出 JDBC 调试日志的效果。
2.1 BaseJdbcLogger
2.1.1 数据结构
protected static final Set SET_METHODS = new HashSet(); // 记录了PreparedStatement接口中定义的常用的set*()方法
protected static final Set EXECUTE_METHODS = new HashSet(); // 记录了Statement接口和PreparedStatement接口中与执行SQL语句相关的方法
private Map columnMap = new HashMap(); // 记录了PreparedStatement.set*()方法设置的键值对
private List columnNames = new ArrayList(); // 记录了PreparedStatement.set*()方法设置的key值
private List columnValues = new ArrayList(); // 记录了PreparedStatement.set*()方法设置的value值
protected Log statementLog; // 用于输出日志的Log对象
protected int queryStack; // 记录了SQL的层数,用于格式化输出SQL
SET_METHODS
:记录了PreparedStatement接口中定义的常用的set*()方法。
EXECUTE_METHODS
:记录了Statement接口和PreparedStatement接口中与执行SQL语句相关的方法。
columnMap
:记录了PreparedStatement.set*()方法设置的键值对。
columnNames
:记录了PreparedStatement.set*()方法设置的key值。
columnValues
:记录了PreparedStatement.set*()方法设置的value值。
statementLog
:用于输出日志的Log对象。
queryStack
: 记录了SQL的层数,用于格式化输出SQL。
在类加载时,会对 SET_METHODS
、EXECUTE_METHODS
进行初始化,源码如下:
static {
SET_METHODS.add("setString");
SET_METHODS.add("setNString");
SET_METHODS.add("setInt");
SET_METHODS.add("setByte");
SET_METHODS.add("setShort");
SET_METHODS.add("setLong");
SET_METHODS.add("setDouble");
SET_METHODS.add("setFloat");
SET_METHODS.add("setTimestamp");
SET_METHODS.add("setDate");
SET_METHODS.add("setTime");
SET_METHODS.add("setArray");
SET_METHODS.add("setBigDecimal");
SET_METHODS.add("setAsciiStream");
SET_METHODS.add("setBinaryStream");
SET_METHODS.add("setBlob");
SET_METHODS.add("setBoolean");
SET_METHODS.add("setBytes");
SET_METHODS.add("setCharacterStream");
SET_METHODS.add("setNCharacterStream");
SET_METHODS.add("setClob");
SET_METHODS.add("setNClob");
SET_METHODS.add("setObject");
SET_METHODS.add("setNull");
EXECUTE_METHODS.add("execute");
EXECUTE_METHODS.add("executeUpdate");
EXECUTE_METHODS.add("executeQuery");
EXECUTE_METHODS.add("addBatch");
}
2.1.2 构造函数
public BaseJdbcLogger(Log log, int queryStack) {
this.statementLog = log;
if (queryStack == 0) {
this.queryStack = 1;
} else {
this.queryStack = queryStack;
}
}
初始化 Log 对象,指定 SQL 层次。
2.1.3 方法功能
BaseJdbcLogger
里大部分方法比较简单,要特别关注的是 #getParameterValueString()
和 #prefix()
方法。
String getParameterValueString()
【功能】获取参数值字符串,其形式为 value1(type1), value2(type2), ..., null, ...
,用于输出实际执行 SQL 时绑定参数的信息。 【源码与注解】
protected String getParameterValueString() {
List typeLists = new ArrayList(columnValues.size());
// 遍历所有绑定传入的参数值,若为空则字符串为 "null",否则为 "value(type)"
for (Object value : columnValues) {
if (value == null) {
typeLists.add("null");
} else {
typeLists.add(value + "(" + value.getClass().getSimpleName() + ")");
}
}
// eg: [null, 10(Integer), test(String)]
// 将 List 中的值转化为字符串
final String parameters = typeLists.toString();
// 去掉前后中括号
return parameters.substring(1, parameters.length() - 1);
}
String prefix(boolean isInput)
【功能】根据判断输入还是输出阶段输出的日志,决定日志前缀是形如 "==> " 还是 "<== "。 【源码与注解】
protected void debug(String text, boolean input) {
if (statementLog.isDebugEnabled()) {
statementLog.debug(prefix(input) + text);
}
}
protected void trace(String text, boolean input) {
if (statementLog.isTraceEnabled()) {
statementLog.trace(prefix(input) + text);
}
}
private String prefix(boolean isInput) {
char[] buffer = new char[queryStack * 2 + 2];
Arrays.fill(buffer, '=');
buffer[queryStack * 2 + 1] = ' '; // 前缀最后一个字符为空格,与实际的日志内容隔开
if (isInput) {
buffer[queryStack * 2] = '>'; // 根据输入还是输出决定前缀的第一个字符为 ‘<’ 还是倒数第二个字符为 ‘>’
} else {
buffer[0] = '<';
}
return new String(buffer);
}
String removeBreakingWhitespace(String original)
【功能】将多行字符串整合成一行,并且取出多余的空格,主要是将 SQL 格式化,方便日志输出。 【源码与注解】
protected String removeBreakingWhitespace(String original) {
// 根据字符串中的 \t\n\r\f 解析处理,分成多段
StringTokenizer whitespaceStripper = new StringTokenizer(original);
StringBuilder builder = new StringBuilder();
// 将每一段拼接起来,并用空格分隔
while (whitespaceStripper.hasMoreTokens()) {
builder.append(whitespaceStripper.nextToken());
builder.append(" ");
}
return builder.toString();
}
【测试案例】
public class Test extends BaseJdbcLogger {
public Test(Log log, int queryStack) {
super(log, queryStack);
}
public static void main(String[] args) {
String sql = "select id, role_name as roleName, note " +
"from t_role\r\n" +
"where role_name like concat('%', #{roleName}, '%')";
Test test = new Test(null, 0);
System.out.println(test.removeBreakingWhitespace(sql));
}
}
执行结果如下:
2.2 ConnectionLogger
【功能】ConnectionLogger
继承了 BaseJdbcLogger 并实现了 InvocationHandler 接口,封装了真正的 Connection 对象,并为其生成代理对象。 【构造方法和成员】
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
private Connection connection;
private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.connection = conn;
}
// other code
}
【生成代理对象方法】
// 创建一个内嵌Log对象的代理Connection对象
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
【代理逻辑】
@Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
// 如果调用的是从Object继承的方法,则直接调用,不做任何其他处理
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// 如果调用的是preparedStatement()方法、prepareCall()方法或createStatement()方法,
// 则在创建相应的statement对象后,为其创建代理对象并返回该代理对象
if ("prepareStatement".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
// 调用底层封装的Connection对象的prepareStatement()方法,得到PreparedStatement对象
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else {
// 其他方法则直接调用底层Connection对象的相应方法
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 返回真实的对象
public Connection getConnection() {
return connection;
}
}
【解析】
(1)ConnectionLogger
封装了 Connection 对象,在构造函数中,调用父类构造器用传入的Log 对象和 SQL 层次进行初始化。
(2)#newInstance()
方法负责暴露给调用者生成代理对象。
(3)当执行生成的代理 Connection 对象的方法时,会调用 #invoke()
方法,在该方法中,对执行的方法进行拦截,若是 Object 类中声明的方法,直接反射调用,不做特殊处理,如果是 prepareStatement、prepareCall、createStatement 方法,为方法调用返回的 PreparedStatement 、Statement 对象生成响应的代理对象再返回,因为 prepareStatement、prepareStatement 方法的入参是 SQL,所以这里打印 SQL 的日志,开发者测试时就可以从日志中看到实际执行的 SQL 是什么,特别是使用动态 SQL 时,很难看出实际执行的 SQL 是什么。
【测试案例】
调用代码(部分)
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtil.openSqlSession("mybatis-config-properties.xml");
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(1L);
System.out.println("Role = " + role);
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
mapper 配置(部分)
select id, role_name as roleName, note
from t_role
where id = #{id}
在 ConnectionLogger
里的 #invoke()
方法里打上断点,运行观察
可以看到,执行 SQL 时会调用 Connection.prepareStatement()
创建 PreparedStatement 对象,并打印一行日志输出取出多余换行空格后的 SQL。
2.3 PreparedStatementLogger
【功能】PreparedStatementLogger
继承了 BaseJdbcLogger 并实现了 InvocationHandler 接口,封装了真正的 PreparedStatement 对象,并提供了 #newInstance()
方法生成代理对象,会被 ConnectionLogger 所调用。 【代理逻辑】
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// (1) 如果调用 PreparedStatement 的执行方法
if (EXECUTE_METHODS.contains(method.getName())) {
// (1.1) 输出绑定参数信息
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true);
}
clearColumnInfo();
// (1.2 )执行查询SQL,返回值是一个ResultSet对象,封装生成代理对象ResultSetLogger返回
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
// (1.3) 执行其他方法不用封装代理对象返回
return method.invoke(statement, params);
}
// (2) 如果执行的是设置参数的方法,将传入参数保存到父类成员中,方便执行时输出参数日志
} else if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
// (3) getResultSet() 一般跟 execute() 方法配合使用,用来获取SQL执行的结果集
} else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
// (4) getUpdateCount() 一般跟 execute() 方法配合使用,用来获取SQL执行的更新计数
} else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
【解析】 (1) 如果调用 PreparedStatement 的执行方法 在 BaseJdbcLogger
中,存储了四个执行方法的名称,分别为:execute
、executeUpdate
、executeQuery
、addBatch
。
execute
是一个通用的执行 SQL 的方法,返回值为布尔值,如果为 true,表示返回一个结果集(常见于查询语句),如果为 false,则返回表示没有结果或者是更新计数;
executeUpdate
可以执行 INSERT、UPDATE 或 DELETE 语句,返回值是一个整数表示影响行数;
executeQuery
通常用来执行 SELECT 语句,返回值是一个结果集;
addBatch
是往 Statement 对象中添加要批量执行的 SQL;
(1.1)在执行上述这些方法时,通常都是会绑定参数的,所以如果检查到执行上述的方法,会输出绑定参数的日志。 (1.2)如果执行 executeQuery
肯定返回一个 ResultSet,封装成代理对象返回。 (1.3)其他执行方法直接反射调用。
(2)如果执行的是设置参数的方法,将传入参数保存到父类成员中,方便执行时输出参数日志。 (3)getResultSet() 一般跟 execute() 方法配合使用,用来获取SQL执行的结果集,这里生成结果集的代理对象返回。 (4)getUpdateCount() 一般跟 execute() 方法配合使用,用来获取SQL执行的更新计数。
【测试案例】 还是上面的代码,在 PreparedStatementLogger
中打上断点,执行情况如下图所示:
可以看到执行的方法是 execute()
,并输出了 SQL 绑定参数的日志。
2.4 ResultLogger
【功能】ResultLogger
继承了 BaseJdbcLogger 并实现了 InvocationHandler 接口,封装了真正的 ResultSet 对象,并提供了 #newInstance()
方法生成代理对象,会被 PreparedStatementLogger 和 StatementLogger 所调用。 【数据结构与构造】
private static Set BLOB_TYPES = new HashSet(); // 不输出字段值类型,主要是超大数据的类型
private boolean first = true; // 控制输出结果集字段名的日志时,只在第一次打印日志
private int rows = 0; // 统计结果集中有多少行
private ResultSet rs; // 封装的真实ResultSet对象
private Set blobColumns = new HashSet(); // 存储属于大数据类型的字段名
static {
BLOB_TYPES.add(Types.BINARY);
BLOB_TYPES.add(Types.BLOB);
BLOB_TYPES.add(Types.CLOB);
BLOB_TYPES.add(Types.LONGNVARCHAR);
BLOB_TYPES.add(Types.LONGVARBINARY);
BLOB_TYPES.add(Types.LONGVARCHAR);
BLOB_TYPES.add(Types.NCLOB);
BLOB_TYPES.add(Types.VARBINARY);
}
private ResultSetLogger(ResultSet rs, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.rs = rs;
}
ResultLogger
具备打印结果集中每行数据信息日志的功能,需要将日志级别设定为 TRACE
,需要在 log4j.properties 文件中添加以下配置:
log4j.logger.ssm=TRACE
其中,后缀 ssm 是映射配置文件中 标签的 namespace 属性的前缀即可。 ResultLogger
限制了一些可能携带大量数据的字段的数据的输出,BLOB_TYPES
就存储了受限制的类型,并在加载类时静态初始化,其他字段的含义看注释即可。
【代理逻辑】
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
Object o = method.invoke(rs, params);
// 调用 ResultSet.next(),遍历结果及中的多行结果
if ("next".equals(method.getName())) {
if (((Boolean) o)) {
rows++; // 统计行数加一
// 需要开启TRACE的日志级别才能输出每一行的字段信息和对应的值信息
if (isTraceEnabled()) {
ResultSetMetaData rsmd = rs.getMetaData();
final int columnCount = rsmd.getColumnCount();
// 字段信息只需要输出一次日志
if (first) {
first = false;
printColumnHeaders(rsmd, columnCount);
}
// 输出每行数据中的字段值
printColumnValues(columnCount);
}
} else {
debug(" Total: " + rows, false);
}
}
clearColumnInfo();
return o;
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
当 ResultSet.next()
被调用时,类似迭代器会遍历结果集中的多行结果,假如返回 false,表示没有行了,则打印统计行数的日志;假如返回 true,则获取行信息,打印其字段信息和字段值,对应 #printColumnHeaders()
和 #printColumnValues()
方法,还是上面的测试案例,加上 TRACE 的配置之后,输出的日志如下:
2.5 StatementLogger
跟 PreparementLogger
类似,不赘述。
你可能感兴趣的:([MyBatis源码分析 - 日志模块])
【模块】Non-local Neural
dearr__
扒网络模块 深度学习 pytorch python
论文《Non-localNeuralNetworks》作用非局部神经网络通过非局部操作捕获长距离依赖,这对于深度神经网络来说至关重要。这些操作允许模型在空间、时间或时空中的任何位置间直接计算相互作用,从而捕获长距离的交互和依赖关系。这种方法对于视频分类、对象检测/分割以及姿态估计等任务表现出了显著的改进。机制非局部操作通过在输入特征图的所有位置上计算响应的加权和来实现,其中权重由位置之间的关系(如
【模块】GNConv卷积模块
dearr__
扒网络模块 深度学习 python pytorch
论文《GCNet:Non-localNetworksMeetSqueeze-ExcitationNetworksandBeyond》1、作用GCNet(GlobalContextNetwork)结合了非局部网络(Non-LocalNetwork,NLNet)的长距离依赖捕捉能力和Squeeze-ExcitationNetwork(SENet)的轻量级特性,有效地建模全局上下文信息。通过简化非局部块
列表推导式与生成器表达式(深度实战版)
Bruce_xiaowei
笔记 编程 总结经验 python 数据结构
列表推导式与生成器表达式(深度实战版)一、列表推导式:不只是语法糖1.底层原理剖析#编译后的字节码对比deftraditional_loop():result=[]foriinrange(5):result.append(i*2)returnresultdeflist_comprehension():return[i*2foriinrange(5)]#使用dis模块查看字节码差异importdis
最详细的Windows下安装node.js 和 npm大全
i建模
前端开发 node.js windows npm javascript typescript 前端框架
说明Node.js®是一个免费、开源、跨平台的JavaScript运行时环境,它让开发人员能够创建服务器Web应用、命令行工具和脚本。NPM的全称是NodePackageManager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。2020年3月17日,Github宣布收购npm,GitHub现在已经保证npm将永远免费。安装时一般要选择LTS版本,也就是长期
适合iis发布执行API定时任务的库和框架有哪些
默萧笙故
API .NET C# c# .net 开发语言
在IIS上发布和执行API定时任务时,您可以考虑以下几种库和框架,这些工具能够提供稳定的任务调度功能并适于与ASP.NET或ASP.NETCore应用程序兼容:1.Hangfire概述:Hangfire是一个非常流行的库,允许在ASP.NET应用程序中创建和管理后台任务。它支持持久化和异步执行。安装:可以通过NuGet包管理器安装。功能:提供任务重试和失败日志的支持。通过可视化仪表板监控任务状态。
**LangChain 核心概念与架构详解**
dgay_hua
langchain 架构 python
简介LangChain是一个强大的框架,专注于构建基于大语言模型(LLM)的应用。本文将深入介绍LangChain的核心架构、关键组件以及其特性。通过理解这些内容,开发者能更高效地构建复杂的LLM应用。LangChain架构解析LangChain的架构由多个包组成,每个包负责特定的功能模块。以下是主要组件的功能和作用:1.核心基础包(langchain-core)此包定义了LangChain的基础
C++ STL容器 —— map/multimap 用法详解
Ember_Sky
C++ 常用STL容器及算法 c++ map系列用法详解
C++STL容器——map/multimap用法详解写在前面:近期正在学习C++的STL容器,因此在这里做一下日志记录,主要介绍一些容器基本成员函数的用法,配上实际用例,并不涉及原理。但别人的博客终究是别人的,最好自己上手操作一下.写的不好,请大神手下留情.下面说的“运行之后”表示:运行上个语句之后的结果.一行如果说的太长的话,就得拖动下面的进度条才能看到后面的内容,非常麻烦因此将一段话分成了多行
Java:动态代理
葡萄_成熟时_
Java基础 java 开发语言
动态代理的特点是无侵入式的给代码增加额外的功能。调用者---->代理---->对象java的动态代理主要用于在运行时动态创建代理对象,从而拦截方法调用,实现AOP(面向切面编程)、权限控制、日志记录等功能。Java通过接口来保证代理,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法创建代理对象java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:publi
limits.conf文件工作原理
weixin_33716154
shell 操作系统
1、limits.conf描述limits.conf文件实际是LinuxPAM(插入式认证模块,PluggableAuthenticationModules)中pam_limits.so的配置文件,突破系统的默认限制,对系统访问资源有一定保护作用。limits.conf和sysctl.conf区别在于limits.conf是针对用户,而sysctl.conf是针对整个系统参数配置。2、limits
Redis 架构与设计
yuguo.im
redis 架构 数据库
Redis是一种开源的高性能内存键值数据库,其架构设计围绕简单、快速和高效展开。1.Redis的核心架构Redis是一个基于C语言编写的单线程内存数据库,但通过多路复用实现了高效的并发处理。它的架构主要由以下几个模块组成:a.客户端与服务器Redis采用客户端-服务器架构:客户端发送命令请求。服务器处理请求并返回结果。通信使用RESP(RedisSerializationProtocol)协议。b
【学写LibreCAD】0 仿写LibreCAD简介
Source.Liu
CXX c++ rust LibreCAD
一、LibreCAD核心模块:核心模块(Core)功能:处理CAD的核心逻辑,如几何计算、图形对象管理、坐标系转换等。关键组件:图形对象:如直线、圆、圆弧、多段线等。数学工具:向量、矩阵、几何计算等。文档管理:管理当前打开的CAD文档及其内容。用户界面模块(UI)功能:提供图形用户界面(GUI),包括菜单、工具栏、绘图区域等。关键组件:主窗口:包含菜单栏、工具栏、状态栏等。绘图区域:显示和编辑CA
rust 初探 -- Package, Crate, Module
Jiangw557
rust rust 开发语言 后端
rust初探–Package,Crate,ModulePackage,Crate,定义Modulerust模块系统Package(最顶层):Cargo的特性,让你构建、测试、共享CrateCrate:一个模块树,它可以产生一个library或可执行文件Module、Use:让你控制代码的组织、作用域、私有路径Path(路径):为struct、function或module等项命名的方式Packag
游戏任务系统模块架构设计分析
你一身傲骨怎能输
射击游戏项目 游戏
1.引言在现代商业化射击游戏中,任务系统是一个至关重要的模块。它不仅能增加游戏的深度和趣味性,还能通过任务奖励机制提高玩家的参与度和留存率。本文将详细介绍一个高扩展、高性能、高可配置的C#语言任务系统模块的架构设计和实现。2.需求分析2.1功能需求任务创建与管理:支持创建、更新、删除任务。任务分配:支持将任务分配给玩家。任务进度跟踪:实时跟踪玩家的任务进度。任务完成与奖励:处理任务完成后的奖励发放
Transformer 代码剖析8 - 编码器模块Encoder (pytorch实现)
lczdyx
Transformer代码剖析 transformer pytorch 深度学习 人工智能 python
一、代码结构总览TransformerEncoder__init__初始化Encoder类forward前向传播super()父类初始化构建词嵌入层self.emb=TransformerEmbedding参数:d_model/max_len/vocab_size/drop_prob/device构建编码层堆栈self.layers=nn.ModuleList循环创建n_layers个Encode
【11】RUST使用cargo组织crate
憨憨2号
RUST rust 开发语言
文章目录使用cargo组织crate重导出编译文档生成测试cargo组织工作空间TODOcrate.io账号TODO暂时不看发布crate使用cargo组织crate重导出在模块顶部使用pubuseself::重导出,方便使用模块时候直接使用usemod_X::xxx。从而隐藏crate内部模块的结构。方便向外部暴露接口。mod_x.rs如下:pubuseself::mod_A:fun_01;pu
Oracle 12CR2 中alert.log出现大量的 WARNING: too many parse errors 告警
weixin_34007886
数据库 运维
Oracle12CR2中alert.log出现大量的WARNING:toomanyparseerrors告警日志如下:2018-06-24T17:16:21.024586+08:00WARNING:toomanyparseerrors,count=3800SQLhash=0x03b77f5bPARSEERROR:ospid=6223,error=1031forstatement:2018-06-2
alter日志报WARNING: too many parse errors
weixin_30480075
数据库版本:12.2.0操作系统版本:RHEL7.2最近观察到一个数据库alert日志老是报硬解析太多错误,且对应的sql语句都是查看数据字典表:2017-06-16T08:46:46.417468+08:00TTEST(4):WARNING:toomanyparseerrors,count=100SQLhash=0x03b29074TTEST(4):PARSEERROR:ospid=3504,e
Hutool - Script:脚本执行封装,以 JavaScript 为例
五行星辰
业务系统应用技术 javascript 开发语言 java 后端
一、简介在Java开发中,有时需要动态执行脚本代码,比如JavaScript脚本,来实现一些灵活的业务逻辑,如动态规则计算、数据处理等。Java本身提供了javax.script包来支持脚本执行,但使用起来较为繁琐。Hutool-Script模块对Java的脚本执行功能进行了封装,提供了更简洁易用的API,让开发者可以方便地执行各种脚本,这里主要介绍JavaScript脚本的执行。二、引入依赖如果
Java 9模块与Maven的深度结合
t0_54program
java maven python 个人开发
在Java9引入模块化之后,如何将模块化与Maven项目结合成为了许多开发者关注的焦点。本文将通过一个简单的示例,展示如何在Maven项目中开发Java9模块,并使用非模块化的外部库(如Jsoup)。1.Maven项目配置首先,我们需要创建一个Maven项目,并在pom.xml中配置相关的依赖和插件。以下是完整的pom.xml文件内容:4.0.0com.logicbig.examplejava9-
基于Spring Boot的驾校预约管理系统
超级无敌暴龙战士塔塔开
Java课设与毕设资源 spring boot java mybatis
文章目录项目介绍主要功能截图:登录首页学员管理教练管理车辆管理关系管理车辆维修模块个人中心部分代码展示设计总结项目获取方式作者主页:Java韩立简介:Java领域优质创作者、简历模板、学习资料、面试题库【关注我,都给你】文末获取源码联系项目介绍基于SpringBoot的驾校预约管理系统(可帮忙远程调试),java项目。eclipse和idea都能打开运行。推荐环境配置:eclipse/ideajd
松灵机器人地盘 安装 ros 驱动 并且 发布ros 指令进行控制
luoganttcc
机器人 机器人
安装驱动$cd~/catkin_ws/src$gitclonehttps://github.com/agilexrobotics/ugv_sdk.git$gitclonehttps://github.com/agilexrobotics/scout_ros.git$cd..$catkin_make安装●使能gs_usb内核模块●设置500k波特率和使能can-to-usb适配器sudomodpro
玩转Java微服务:架构设计与实战进阶终极指南
Light60
低代码 实战篇 技能篇 Java微服务 Spring Cloud 分布式架构 容器化部署 服务治理
摘要本文深入解析Java微服务核心原理与落地实践,涵盖SpringCloudAlibaba生态体系实战技巧。通过全链路代码示例演示服务注册发现、分布式配置、熔断限流等关键模块实现,结合电商案例阐述DDD领域驱动设计在微服务拆分的应用。提供可落地的性能优化方案与云原生演进路线,助您构建高可用、易扩展的现代化分布式系统。关键词:Java微服务、SpringCloud、分布式架构、容器化部署、服务治理一
C语言 第二章 (2)
点纭
c语言 开发语言
目录流程控制结构顺序结构分支结构if_else语句单分支双分支多分支实例switch_case语句实例流程控制结构定义流程控制结构是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。程序设计中规定的三种流程结构,即:顺序结构程序从上到下逐行地执行,中间没有任何判断和跳转。分支结构根据条件,选择性地执行某段代码。有if…else和switch-case两种分支语句。循环
Redis数据结构——简单动态字符串
去图书馆睡还不如在宿舍睡
Redis redis 数据结构 数据库
1、简单动态字符串redis没有直接用C语言传统的字符串(以空字符结尾的字符数组)表示,而是自己构建了一种名为简单动态字符串(SDS)的抽象类型,并将SDS用作redis的默认字符串表示。在redis里面,C字符串只会作为字符串字面量用在一些无需对字符串值修改的地方,比如打印日志:redisLog(REDIS_WAINING,"Rdeisisnowreadytoexit,byebye...")当r
Python--内置模块和开发规范(上)
索然无味io
Python安全开发 python 开发语言 windows 网络安全 web安全 笔记
1.内置模块1.1JSON模块核心功能序列化:Python数据类型→JSON字符串importjsondata=[{"id":1,"name":"武沛齐"},{"id":2,"name":"Alex"}]json_str=json.dumps(data,ensure_ascii=False)#禁用ASCII转义反序列化:JSON字符串→Python数据类型data_list=json.loads(
Simulink开发项1000例实战专栏--实例140:构建一个完整的机器人视觉导航系统模型
xiaoheshang_123
MATLAB 开发项目实例 1000 例专栏 手把手教你学 MATLAB 专栏 计算机视觉 人工智能 simulink
目录基于Simulink的机器人视觉导航系统设计与仿真1.背景介绍1.1项目背景2.系统建模与仿真2.1视觉导航系统组成2.2图像采集与处理2.3路径规划2.4运动控制2.5传感器融合3.Simulink仿真模型3.1创建Simulink模型3.2添加模块3.2.1图像采集模块3.2.2图像处理模块3.2.3路径规划模块3.2.4运动控制模块3.3连接模块3.4设置仿真参数4.示例代码片段5.结束
Everything-文件查找软件制作
风栖柳白杨
软件制作 pyqt 数据库 YOLO
写在前边:随着电脑里边的东西越来越多,很多东西放的杂七杂八;今天实在忍不了了,一怒之下,突发奇想,做一个类似Everything的文件查找软件,现在共享出来。一、软件展示二、源码展示与讲解1、所用到的组件(1)2、源码分块细嗦(1)导入模块importsys#通常用于操作系统相关的任务,例如命令行参数和系统退出importos#用于与操作系统交互,例如文件和目录操作importfnmatch#用于
Webpack、Vite区别知多少?
m0_74824044
面试 学习路线 阿里巴巴 webpack 前端 node.js
前端的项目打包,我们常用的构建工具有Webpack和Vite,那么Webpack和Vite是两种不同的前端构建工具,那么你们又是否了解它们的区别呢?我们在做项目时要如何选择呢?一、工具定义1、Webpack:是一个强大的静态模块打包工具,它可以将各种类型的文件,如JavaScript、CSS、图片等,作为模块进行打包,并生成最终的静态资源文件。Webpack使用各种loader和plugin来处理
淘天业务技术2024年度热门文章盘点
阿里巴巴淘系技术团队官网博客
1.Agent调研--19类Agent框架对比本文主要探讨了19类Agent框架的对比,介绍了单智能体和多智能体框架的特点与应用,详细分析了几种主流的Agent框架,分别从任务规划、模型选择、执行任务等方面进行了阐述;最后分享了多智能体系统的优点和挑战。阅读全文2.AI助手:淘宝交易研发效率提升50%本文介绍了AI助理在提升交易系统研发效率中的应用。通过智能日志分析、自动化测试数据生成和经验沉淀,
将Javascript打包成exe可执行文件
海上彼尚
node.js javascript 开发语言 node.js
目录什么是pkg?安装pkg基本使用步骤1:准备你的Node.js项目步骤2:通过命令行打包步骤3:运行生成的可执行文件配置package.json处理资源文件高级用法指定Node.js版本和平台打包整个项目处理环境变量常见问题问题1:动态导入模块失败问题2:文件路径错误问题3:缺少依赖示例:打包Express应用总结什么是pkg?pkg是一个命令行工具,能将Node.js项目及其依赖打包成一个单
jquery实现的jsonp掉java后台
知了ing
java jsonp jquery
什么是JSONP?
先说说JSONP是怎么产生的:
其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。
1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;
2、
Struts2学习笔记
caoyong
struts2
SSH : Spring + Struts2 + Hibernate
三层架构(表示层,业务逻辑层,数据访问层) MVC模式 (Model View Controller)
分层原则:单向依赖,接口耦合
1、Struts2 = Struts + Webwork
2、搭建struts2开发环境
a>、到www.apac
SpringMVC学习之后台往前台传值方法
满城风雨近重阳
springMVC
springMVC控制器往前台传值的方法有以下几种:
1.ModelAndView
通过往ModelAndView中存放viewName:目标地址和attribute参数来实现传参:
ModelAndView mv=new ModelAndView();
mv.setViewName="success
WebService存在的必要性?
一炮送你回车库
webservice
做Java的经常在选择Webservice框架上徘徊很久,Axis Xfire Axis2 CXF ,他们只有一个功能,发布HTTP服务然后用XML做数据传输。
是的,他们就做了两个功能,发布一个http服务让客户端或者浏览器连接,接收xml参数并发送xml结果。
当在不同的平台间传输数据时,就需要一个都能解析的数据格式。
但是为什么要使用xml呢?不能使json或者其他通用数据
js年份下拉框
3213213333332132
java web ee
<div id="divValue">test...</div>测试
//年份
<select id="year"></select>
<script type="text/javascript">
window.onload =
简单链式调用的实现技术
归来朝歌
方法调用 链式反应 编程思想
在编程中,我们可以经常遇到这样一种场景:一个实例不断调用它自身的方法,像一条链条一样进行调用
这样的调用你可能在Ajax中,在页面中添加标签:
$("<p>").append($("<span>").text(list[i].name)).appendTo("#result");
也可能在HQ
JAVA调用.net 发布的webservice 接口
darkranger
webservice
/**
* @Title: callInvoke
* @Description: TODO(调用接口公共方法)
* @param @param url 地址
* @param @param method 方法
* @param @param pama 参数
* @param @return
* @param @throws BusinessException
Javascript模糊查找 | 第一章 循环不能不重视。
aijuans
Way
最近受我的朋友委托用js+HTML做一个像手册一样的程序,里面要有可展开的大纲,模糊查找等功能。我这个人说实在的懒,本来是不愿意的,但想起了父亲以前教我要给朋友搞好关系,再加上这也可以巩固自己的js技术,于是就开始开发这个程序,没想到却出了点小问题,我做的查找只能绝对查找。具体的js代码如下:
function search(){
var arr=new Array("my
狼和羊,该怎么抉择
atongyeye
工作
狼和羊,该怎么抉择
在做一个链家的小项目,只有我和另外一个同事两个人负责,各负责一部分接口,我的接口写完,并全部测联调试通过。所以工作就剩下一下细枝末节的,工作就轻松很多。每天会帮另一个同事测试一些功能点,协助他完成一些业务型不强的工作。
今天早上到公司没多久,领导就在QQ上给我发信息,让我多协助同事测试,让我积极主动些,有点责任心等等,我听了这话,心里面立马凉半截,首先一个领导轻易说
读取android系统的联系人拨号
百合不是茶
android sqlite数据库 内容提供者 系统服务的使用
联系人的姓名和号码是保存在不同的表中,不要一下子把号码查询来,我开始就是把姓名和电话同时查询出来的,导致系统非常的慢
关键代码:
1, 使用javabean操作存储读取到的数据
package com.example.bean;
/**
*
* @author Admini
ORACLE自定义异常
bijian1013
数据库 自定义异常
实例:
CREATE OR REPLACE PROCEDURE test_Exception
(
ParameterA IN varchar2,
ParameterB IN varchar2,
ErrorCode OUT varchar2 --返回值,错误编码
)
AS
/*以下是一些变量的定义*/
V1 NUMBER;
V2 nvarc
查看端号使用情况
征客丶
windows
一、查看端口
在windows命令行窗口下执行:
>netstat -aon|findstr "8080"
显示结果:
TCP 127.0.0.1:80 0.0.0.0:0 &
【Spark二十】运行Spark Streaming的NetworkWordCount实例
bit1129
wordcount
Spark Streaming简介
NetworkWordCount代码
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
Struts2 与 SpringMVC的比较
BlueSkator
struts2 spring mvc
1. 机制:spring mvc的入口是servlet,而struts2是filter,这样就导致了二者的机制不同。 2. 性能:spring会稍微比struts快。spring mvc是基于方法的设计,而sturts是基于类,每次发一次请求都会实例一个action,每个action都会被注入属性,而spring基于方法,粒度更细,但要小心把握像在servlet控制数据一样。spring
Hibernate在更新时,是可以不用session的update方法的(转帖)
BreakingBad
Hibernate update
地址:http://blog.csdn.net/plpblue/article/details/9304459
public void synDevNameWithItil()
{Session session = null;Transaction tr = null;try{session = HibernateUtil.getSession();tr = session.beginTran
读《研磨设计模式》-代码笔记-观察者模式
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
/**
* “观
重置MySQL密码
chenhbc
mysql 重置密码 忘记密码
如果你也像我这么健忘,把MySQL的密码搞忘记了,经过下面几个步骤就可以重置了(以Windows为例,Linux/Unix类似):
1、关闭MySQL服务
2、打开CMD,进入MySQL安装目录的bin目录下,以跳过权限检查的方式启动MySQL
mysqld --skip-grant-tables
3、新开一个CMD窗口,进入MySQL
mysql -uroot
再谈系统论,控制论和信息论
comsci
设计模式 生物 能源 企业应用 领域模型
再谈系统论,控制论和信息论
偶然看
oracle moving window size与 AWR retention period关系
daizj
oracle
转自: http://tomszrp.itpub.net/post/11835/494147
晚上在做11gR1的一个awrrpt报告时,顺便想调整一下AWR snapshot的保留时间,结果遇到了ORA-13541这样的错误.下面是这个问题的发生和解决过程.
SQL> select * from v$version;
BANNER
-------------------
Python版B树
dieslrae
python
话说以前的树都用java写的,最近发现python有点生疏了,于是用python写了个B树实现,B树在索引领域用得还是蛮多了,如果没记错mysql的默认索引好像就是B树...
首先是数据实体对象,很简单,只存放key,value
class Entity(object):
'''数据实体'''
def __init__(self,key,value)
C语言冒泡排序
dcj3sjt126com
算法
代码示例:
# include <stdio.h>
//冒泡排序
void sort(int * a, int len)
{
int i, j, t;
for (i=0; i<len-1; i++)
{
for (j=0; j<len-1-i; j++)
{
if (a[j] > a[j+1]) // >表示升序
自定义导航栏样式
dcj3sjt126com
自定义
-(void)setupAppAppearance
{
[[UILabel appearance] setFont:[UIFont fontWithName:@"FZLTHK—GBK1-0" size:20]];
[UIButton appearance].titleLabel.font =[UIFont fontWithName:@"FZLTH
11.性能优化-优化-JVM参数总结
frank1234
jvm参数 性能优化
1.堆
-Xms --初始堆大小
-Xmx --最大堆大小
-Xmn --新生代大小
-Xss --线程栈大小
-XX:PermSize --永久代初始大小
-XX:MaxPermSize --永久代最大值
-XX:SurvivorRatio --新生代和suvivor比例,默认为8
-XX:TargetSurvivorRatio --survivor可使用
nginx日志分割 for linux
HarborChung
nginx linux 脚本
nginx日志分割 for linux 默认情况下,nginx是不分割访问日志的,久而久之,网站的日志文件将会越来越大,占用空间不说,如果有问题要查看网站的日志的话,庞大的文件也将很难打开,于是便有了下面的脚本 使用方法,先将以下脚本保存为 cutlog.sh,放在/root 目录下,然后给予此脚本执行的权限
复制代码代码如下:
chmo
Spring4新特性——泛型限定式依赖注入
jinnianshilongnian
spring spring4 泛型式依赖注入
Spring4新特性——泛型限定式依赖注入
Spring4新特性——核心容器的其他改进
Spring4新特性——Web开发的增强
Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC
Spring4新特性——Groovy Bean定义DSL
Spring4新特性——更好的Java泛型操作API
Spring4新
centOS安装GCC和G++
liuxihope
centos gcc
Centos支持yum安装,安装软件一般格式为yum install .......,注意安装时要先成为root用户。
按照这个思路,我想安装过程如下:
安装gcc:yum install gcc
安装g++: yum install g++
实际操作过程发现,只能有gcc安装成功,而g++安装失败,提示g++ command not found。上网查了一下,正确安装应该
第13章 Ajax进阶(上)
onestopweb
Ajax
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
How to determine BusinessObjects service pack and fix pack
blueoxygen
BO
http://bukhantsov.org/2011/08/how-to-determine-businessobjects-service-pack-and-fix-pack/
The table below is helpful. Reference
BOE XI 3.x
12.0.0.
y BOE XI 3.0 12.0.
x.
y BO
Oracle里的自增字段设置
tomcat_oracle
oracle
大家都知道吧,这很坑,尤其是用惯了mysql里的自增字段设置,结果oracle里面没有的。oh,no 我用的是12c版本的,它有一个新特性,可以这样设置自增序列,在创建表是,把id设置为自增序列
create table t
(
id number generated by default as identity (start with 1 increment b
Spring Security(01)——初体验
yang_winnie
spring Security
Spring Security(01)——初体验
博客分类: spring Security
Spring Security入门安全认证
首先我们为Spring Security专门建立一个Spring的配置文件,该文件就专门用来作为Spring Security的配置