log4j
程序员文章站
2022-05-23 23:22:14
...
概述
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程(百度百科-log4j).
Log4j现已更新至2.8.2(也就是Log4j2-index.html)
入门案例
pom.xml
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
helloworldpackage log4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class HelloWorld {
private static final Logger logger = LogManager.getLogger("HelloWorld");
public static void main(String[] args) {
logger.info("Hello, World!");
}
}
访问打印:10:38:51.978 [main] INFO HelloWorld - Hello, World! 简单解释下:log4j的2版本之后,其配置文件名需更改log4j2.xxx,上述的入门案例中,配置info级别的控制台Console日志.
log4j详解
log4j配置属性
log4j2配置支持多种方式properties,xml,json,yaml,以xml配置为例进行讲解
基本架构(xml)
<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<filter ... />
<Appenders>
<appender ... >
<filter ... />
</appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<filter ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>
基本解释
- Loggers:配置不同的logger对象,root表示根对象,logger为普通对象(与root为父子关系,获取不到logger则使用root)
- Appenders:输出形式,console,file....
- filter:过滤
- properties:通用属性命名
此configuration没有进行配置,可以关注其中的两个属性status(与level取值一样,不过针对的是log4j本身内部处理,一般在启动报错时使用)与monitorInterval(间隔一段时间加载log4j2.xml)
输出级别(level)
log4j定义的级别有OFF,FATAL,ERROR,WARN,INFO,DEBUG,TRACE,ALL,各个级别通过都有其对应intLevel,值越小,级别越高
用户可在xml中自定义level级别
<CustomLevels>
<CustomLevel name="NOTICE" intLevel="450" />
</CustomLevels>
注:name是大小写敏感,使用level API获取时name必须一致,name有两种命名规则,一个是按照name(API LogManager.getLogger(String name)),一个按照包名(API LogManager.getLogger(Class class))输出形式(appender)
log4j的输出形式有多种File,Console,Async,JDBC...以常用的Console(主要调试使用),File(记录日志),Async(异步日志)为例
console
属性:
- filter:过滤,通过level,onMatch,onMismath控制(filter)
- layout:输出格式,可以通过PatternLayout进行覆盖定义PatternLayout
- follow.direct:是否输出转移(可以system输出流,以及重定向),两者不能同时使用.此属性基本不使用
- name:名称标识(配置中要设置唯一)
- ignoreExceptions:默认false,设置true时,要设置错误处理Appender-FailoverAppender
- target:控制台输出SYSTEM_OUT or SYSTEM_ERR
示例见入门案例
file
file有多种,FileAppender,RandomAccessFileAppender,RollingFileAppender,RollingRandomAccessFileAppender,file与randomAccessFile的主要区别在写入流上,前者使用BufferedOutputStream,后者则是ByteBuffer + RandomAccessFile,都开启缓存的前提下后着效率有20-200%的提升(appender效率测评),file与rollingFile的区别在生成策略上,前者仅只有一个log文件,后者则是根据策略生成多个日志文件.项目开发中,file用的相对比较少(一次性日志或日志量比较小),持续的日志打印,基本都是使用RollingRandomAccessFileAppender.
randomAccessFileAppender
这里重点关注几个属性
- append:是否追加,一般设置true,不然每次都会覆盖
- immediateFlush:是否立即刷新,同步日志用的多些
- bufferSize:缓存量
这个appender应用最为广泛,重点解释下
重点关注strategy(TriggeringPolicies)与policy(RolloverStrategies)两个属性,strategy指的是日志rollover(日志合并,比如讲一天的日志压缩一个zip)策略,policy指触发生成新文件策略.以压缩zip为例
policy
- CompositeTriggeringPolicy:通过Policies定义
- CronTriggeringPolicy:cron表达式(cron expression)触发
- OnStartupTriggeringPolicy:初始文件创建,日志创建时间晚于jvm启动时间,则生成一个文件
- SizeBasedTriggeringPolicy:容量触发,设置size,超过则触发(针对单个文件大小)
- TimeBasedTriggeringPolicy :周期性触发压缩,interval设置周期间隔(默认1,没有单位,单位已pattern的最小时间单元),modulate-时间调整,为true,将以0点作为边界偏移(false以第一个file文件生成时间为边界偏移)--此属性要配置filePattern使用
常用SizeBasedTriggeringPolicy,TimeBasedTriggeringPolicy
strategy常用DefaultRolloverStrategy,DirectWriteRolloverStrategy用的少
DefaultRolloverStrategy属性
- fileIndex:默认min,可选max,针对pattern中的%i属性,设置min,最新的日志文件i最小,max反之
- min:最小压压缩文件(此处指zip)数,默认1
- max:最大压缩文件数,默认无上限,设置了达到阈值将会删除最老的一个
- compressionLevel:压缩级别0-9.只对zip有效
当不配置DefaultRolloverStrategy,默认的max=7,值得一提的是,当同时配置SizeBasedTriggeringPolicy,TimeBasedTriggeringPolicy只要满足一个就可以,当只满足SizeBasedTriggeringPolicy,生成的压缩文件数为max-1.
异步日志是log4j2的一个特色,另外单独开一个线程记录日志,但是是否能提升性能应针对具体的应用场景(Log4j异步)
重点关注其中的bufferSize,记录日志事件的队列长度,当值设置过小时,当日志的打印到文件的速度赶不上调用端传给AsyncAppender的速度时, AsyncAppender就把未输出到文件的日志信息存放到这个队列中,当这个队列塞满后,AsyncAppender就会丢弃新传入的日志信息.
其基本使用配置,在Appenders>配置
<Async name="Async">
<AppenderRef ref="MyFile"/>
</Async>
PatternLayout
其参数格式太多,仅列基础参考
%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
%d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%r: 输出自应用启动到输出该log信息耗费的毫秒数
%c: 输出日志信息所属的类目,通常就是所在类的全名
%t: 输出产生该日志事件的线程名
%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%: 输出一个"%"字符
%F: 输出日志消息产生时所在的文件名称
%L: 输出代码中的行号
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行
可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。
如:
%20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,默认的情况下右对齐。
%-20c:指定输出category的名称,最小的宽度是20,如果category的名称小于20的话,"-"号指定左对齐。
%.30c:指定输出category的名称,最大的宽度是30,如果category的名称大于30的话,就会将左边多出的字符截掉,但小于30的话也不会有空格。
%20.30c:如果category的名称小于20就补空格,并且右对齐,如果其名称长于30字符,就从左边较远输出的字符截掉。
归类与级别控制
好的日志管理必须实现日志的归类存储,横向归类一般指业务归类,纵向归类就是级别的归类,不同的级别应有独立的处理.
归类
示例
<loggers>
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="info" />
<appender-ref ref="error" />
</root>
<logger name="serviceOne" level="info" additivity="fasle">
<appender-ref ref="serviceOne" />
</logger>
</loggers>
通过loggers设置所有的logger对象,root是根对象,logger是普通对象,当使用api获取logger的时候,当得不到logger,会使用root logger,并执行root里面所有的appender.级别控制,logger中的additivity=false表示不传递给父logger处理(此处指的root logger)
级别控制
级别的过滤控制主要通过level与filter实现,level有继承性,即子标签的level会继承父类的标签
level与filter
有level的标签Logger,Filter有ThresholdFilter,BurstFilter(用户控制日志时间的 处理速率,它会在日志事件达到最大限制是丢弃日志事件filter),关注其中的门槛过滤器ThresholdFilter,此filter是对logger级别的补充.
ThresholdFilter
filter可以配置在configuration,appender,logger,配置多个用fiters标签包裹
onMatch与onMismatch都有三个返回值(返回值决定是否继续向下传递)
- ACCEPT:接收,表示有此过滤器处理,进入下个级别的过滤器
- DENY :丢弃,终止在同级的过滤器中传递,在configuration或logger中丢弃后,其他也不会处理
- NEUTRAL:中立 表示会传递给同级别的过滤器处理(也就是同一个一个filters标签下的filter)
log4j使用
基本使用
基本配置
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error" monitorInterval="300">
<properties>
<property name="LOG_HOME">C:/Users/admin/Desktop/temp</property>
</properties>
<appenders>
<!-- 控制台打印,调试使用 -->
<Console name="console" target="SYSTEM_OUT">
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY" />
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-4p] [%l] - %m%n" />
</Console>
<RollingRandomAccessFile name="error"
immediateFlush="true" fileName="${LOG_HOME}/error.log" append="true"
bufferSize="1024" filePattern="${LOG_HOME}/error/%d{yyyy-MM-dd}/%d{HH}-%i.log.zip">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-4p] [%l] - %m%n" />
<!--只处理error级别及以上 -->
<ThresholdFilter level="error" onMatch="ACCEPT"
onMismatch="DENY" />
<Policies>
<!--每个文件不超过100M -->
<SizeBasedTriggeringPolicy size="100Mb" />
<!--每个24小时进行一次压缩zip -->
<TimeBasedTriggeringPolicy interval="24"
modulate="true" />
</Policies>
<!--最多只保存100压缩文件,结合上面的24小时压缩一次,也就是只保留100天的操作日志 -->
<DefaultRolloverStrategy max="100" />
</RollingRandomAccessFile>
<RollingRandomAccessFile name="info"
immediateFlush="false" fileName="${LOG_HOME}/info.log" append="true"
bufferSize="10240" filePattern="${LOG_HOME}/info/%d{yyyy-MM-dd/%d{HH}-%i.log.zip">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-4p] [%l] - %m%n" />
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY" />
<Policies>
<SizeBasedTriggeringPolicy size="100M" />
<TimeBasedTriggeringPolicy interval="24"
modulate="true" />
</Policies>
<DefaultRolloverStrategy max="100" />
</RollingRandomAccessFile>
<!-- 业务logger -->
<Console name="serviceOne" target="SYSTEM_OUT">
<Filters>
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY" />
</Filters>
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-4p] [%l] - %m%n" />
</Console>
<!-- 异步日志 -->
<Async name="asyncService">
<AppenderRef ref="asyncService" />
</Async>
<RollingRandomAccessFile name="asyncService"
immediateFlush="false" fileName="${LOG_HOME}/asyncService.log" append="true"
bufferSize="10240" filePattern="${LOG_HOME}/info/%d{yyyy-MM-dd/%d{HH}-%i.log.zip">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%-4p] [%l] - %m%n" />
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY" />
<Policies>
<SizeBasedTriggeringPolicy size="100M" />
<TimeBasedTriggeringPolicy interval="24"
modulate="true" />
</Policies>
<DefaultRolloverStrategy max="100" />
</RollingRandomAccessFile>
</appenders>
<loggers>
<root level="info">
<!--根处理logger -->
<appender-ref ref="console" />
<appender-ref ref="info" />
<appender-ref ref="error" />
</root>
<logger name="serviceOne" level="info" additivity="false">
<appender-ref ref="serviceOne" />
</logger>
<logger name="asynclog" level="info" additivity="false">
<AppenderRef ref="asyncService" />
</logger>
</loggers>
</Configuration>
log4j在代码中可显示调用
package log4j;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class HelloWorld {
/*获取logger
*通过name获取,在logger标签中以name进行匹配(name区分大小写),没有将使用root logger
*通过class匹配,最终class将转换为package.Class作为name进行匹配(此处就是log4j.HelloWorld,但匹配规则与前者不一样,是根据包名来匹配)
* */
private static final Logger logger = LogManager.getLogger("serviceOne");
public static void main(String[] args) {
/*logger使用api很广泛,摘取几处简单说明*/
/*调用不同的level*/
logger.trace("trace");
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
logger.fatal("fatal");
/*输出不同的内容格式*/
}
}
mybatis 打印日志
搭建个简单的mybatis运行环境(mybatis log):
mybatis数据库新建表blog,内置id,name两个字段
pom.xml引入
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
</dependency>
blogMapperpackage mybatis;
import org.apache.ibatis.annotations.Select;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
blog
package mybatis;
public class Blog {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
mybtis-config.xml<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="LOG4J2" />
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true" />
<property name="username" value="root" />
<property name="password" value="123" />
</dataSource>
</environment>
</environments>
<mappers>
<package name="mybatis" />
<!-- 采用注解 -->
<!-- <mapper resource="org/mybatis/example/BlogMapper.xml"/> -->
</mappers>
</configuration>
log4j2.xml中<loggers>标签下添加 <logger name="mybatis.BlogMapper" level="TRACE" additivity="false">
<AppenderRef ref="mybatis" />
</logger>
<appenders>标签下添加 <!-- mybatis logger -->
<Console name="mybatis" target="SYSTEM_OUT">
<Filters>
<ThresholdFilter level="TRACE" onMatch="ACCEPT"
onMismatch="DENY" />
</Filters>
<PatternLayout pattern="[%d{yyyy-MM-dd}] [%-4p] [%l] - %m%n" />
</Console>
testpackage mybatis;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class TestMyBbatis {
public static void main(String[] args) throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(1);
System.out.println(blog.getName());
} finally {
session.close();
}
//结果打印
/* [2017-08-18] [DEBUG] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159)] - ==> Preparing: SELECT * FROM blog WHERE id = ?
[2017-08-18] [DEBUG] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159)] - ==> Parameters: 1(Integer)
[2017-08-18] [TRACE] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger.trace(BaseJdbcLogger.java:165)] - <== Columns: id, name
[2017-08-18] [TRACE] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger.trace(BaseJdbcLogger.java:165)] - <== Row: 1, 张三
[2017-08-18] [DEBUG] [org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159)] - <== Total: 1
*/
}
}
hibernate 打印日志
搭建hibernate运行环境(hibernate log,官网的配置不生效(hibernate 官方log),尴尬)
pom.xml中新增
<!--hibernate所需依赖 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.1.3.Final</version>
</dependency>
modelpackage hibernate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Blog {
private Integer id;
private String name;
private Integer age;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
hibernate.cfg.xml<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库连接配置 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true</property>
<property name="connection.username">root</property>
<property name="connection.password">123</property>
<!-- 数据库连接池的大小 -->
<property name="connection.pool_size">5</property>
<!-- 每次从数据库中取出并放到JDBC的Statement中的记录条数。Fetch Size设的越大,读数据库的次数越少,速度越快,Fetch
Size越小,读数据库的次数越多,速度越慢 -->
<property name="jdbc.fetch_size">50 </property>
<!--批量插入,删除和更新时每次操作的记录数。Batch Size越大,批量操作的向数据库发送Sql的次数越少,速度就越快,同样耗用内存就越大 -->
<property name="jdbc.batch_size">23 </property>
<!-- SQL 方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- 是否显示sql -->
<property name="show_sql">true</property>
<!-- 是否格式化sql -->
<property name="format_sql">true</property>
<!-- 是否使用注释 -->
<property name="use_sql_comments">false</property>
<!-- 在启动时根据配置更新数据库 -->
<!-- <property name="hbm2ddl.auto">update</property> -->
<!-- 注册我们的实体映射类 -->
<mapping class="hibernate.Blog" />
</session-factory>
</hibernate-configuration>
log4j2.xml中<loggers>新增 <logger name="org.hibernate.type.descriptor.sql" level="TRACE" additivity="false">
<AppenderRef ref="hibernate" />
</logger>
<appenders>标签新增 <!-- hibernate logger -->
<Console name="hibernate" target="SYSTEM_OUT">
<Filters>
<ThresholdFilter level="TRACE" onMatch="ACCEPT"
onMismatch="DENY" />
</Filters>
<PatternLayout pattern="[%d{yyyy-MM-dd}] [%-4p] [%l] - %m%n" />
</Console>
testpackage hibernate;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.criterion.PropertyExpression;
import org.hibernate.criterion.Restrictions;
public class TestHibernate {
public static void main(String[] args) {
//相对于3.x.x版本hibernate,我们在4.x.x采用如下方式获取我们的会话工厂:
//1. 解析我们在hibernate.cfg.xml中的配置
// Configuration configuration = new Configuration().configure();
//2. 创建服务注册类,进一步注册初始化我们配置文件中的属性
// ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
//3. 创建我们的数据库访问会话工厂
// SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
//但在5.1.0版本汇总,hibernate则采用如下新方式获取:
//1. 配置类型安全的准服务注册类,这是当前应用的单例对象,不作修改,所以声明为final
//在configure("cfg/hibernate.cfg.xml")方法中,如果不指定资源路径,默认在类路径下寻找名为hibernate.cfg.xml的文件
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
//2. 根据服务注册类创建一个元数据资源集,同时构建元数据并生成应用一般唯一的的session工厂
SessionFactory sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
/****上面是配置准备,下面开始我们的数据库操作******/
Session session = sessionFactory.openSession();//从会话工厂获取一个session
session.get(Blog.class, 1);
Criteria criteria = session.createCriteria(Blog.class);
criteria.add(Restrictions.eq("name", "lisi"));
criteria.add(Restrictions.eq("age", 1));
criteria.uniqueResult();
session.createQuery("From Blog where name='lisi' and age=1").uniqueResult();
/* Transaction transaction = session.beginTransaction();//开启一个新的事务
transaction.commit();//提交事务
*/
//打印
/* Hibernate:
select
blog0_.id as id1_0_0_,
blog0_.age as age2_0_0_,
blog0_.name as name3_0_0_
from
Blog blog0_
where
blog0_.id=?
[2017-08-18] [TRACE] [org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:65)] - binding parameter [1] as [INTEGER] - [1]
[2017-08-18] [TRACE] [org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:61)] - extracted value ([age2_0_0_] : [INTEGER]) - [2]
[2017-08-18] [TRACE] [org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:61)] - extracted value ([name3_0_0_] : [VARCHAR]) - [张三]
Hibernate:
select
this_.id as id1_0_0_,
this_.age as age2_0_0_,
this_.name as name3_0_0_
from
Blog this_
where
this_.name=?
and this_.age=?
[2017-08-18] [TRACE] [org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:65)] - binding parameter [1] as [VARCHAR] - [lisi]
[2017-08-18] [TRACE] [org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:65)] - binding parameter [2] as [INTEGER] - [1]
[2017-08-18 16:17:14] [INFO] [org.hibernate.hql.internal.QueryTranslatorFactoryInitiator.initiateService(QueryTranslatorFactoryInitiator.java:47)] - HHH000397: Using ASTQueryTranslatorFactory
Hibernate:
select
blog0_.id as id1_0_,
blog0_.age as age2_0_,
blog0_.name as name3_0_
from
Blog blog0_
where
blog0_.name='lisi'
and blog0_.age=1*/
}
}