日志是开发一个系统必不可少的工具,所有问题的解决,最终依赖记录的日志去查询。
开发多年,spring的日志配置我都是复制了别人的配置,从未熟悉。最近梳理了一次。特留一份记录。
个人电脑是mac
java是18
使用idea开发。
日志这块使用了lombok插件。需要自己安装。
例如如下配置将会打印2次日志
删除其中一个stdout即可恢复正常。
初期我拷贝了别人的配置,日志的配置放在src/main/resources/config 下
然后配置如图
该配置通常能正常工作,但是在test时无法正常运行。
因此我修改了配置文件的路径。直接放入 src/main/resources 下。
在webxml也无需加载。强烈建议大家使用,不要放别的地方,增加了出现其他问题的概率。
一) 其实这个功能在spring2x时代就提供了。一直没用过,今天在spring-mvc32x的环境下试验了一次。还算简单易用。
二) 依赖。
spring依赖POI或jExcel来实现对excel输出的支持,前者是apache出品,貌似名气更大,本例使用第一个。
<dependency>
<groupId>orgapachepoi</groupId>
<artifactId>poi</artifactId>
<version>37</version>
</dependency>
三) spring提供了一个AbstractExcelView作为自己实现的视图的父类。实例代码如下。
package yingcarview;
import javatextDateFormat;
import javatextSimpleDateFormat;
import javautilList;
import javautilMap;
import javaxservlet>
import javaxservlet>
import orgapachepoihssfusermodelHSSFDataFormat;
import orgapachepoihssfusermodelHSSFSheet;
import orgapachepoihssfusermodelHSSFWorkbook;
import orgapachepoissusermodelCell;
import orgapachepoissusermodelCellStyle;
import orgapachepoissusermodelIndexedColors;
import orgjodatimeDateTime;
import orgslf4jLogger;
import orgslf4jLoggerFactory;
import orgspringframeworkwebservletviewdocumentAbstractExcelView;
import yingcarbindingDateRange;
import yingcardomainRefuelingRecord;
public class RefuelingRecordExcelView extends AbstractExcelView {
private static final Logger LOGGER = LoggerFactorygetLogger(RefuelingRecordExcelViewclass);
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
@Override
@SuppressWarnings({"unchecked"})
protected void buildExcelDocument(
Map<String, Object> model, // MVC中的M就在这里了
HSSFWorkbook workbook,
>
>
{
("yyyy/MM/dd"));
LOGGERdebug("end: {}", new DateTime(drgetEnd())toString("yyyy/MM/dd"));
}
}
HSSFSheet sheet = workbookcreateSheet(DATE_FORMATformat(drgetStart()) + "-" + DATE_FORMATformat(drgetEnd()));
setColumnsWidth(sheet);
fillTableHeader(workbook, sheet);
fillTableBody(workbook, sheet, rrl);
}
private void setColumnsWidth(HSSFSheet sheet) {
final int[] warr = new int[] {
500, // <空>
4500, // 日期
4500, // 车辆
4500, // 燃油种类
4500, // 燃油单价
4500, // 加油方式
4500, // 加油量
3000, // 花费
12000 // 备注
};
for (int i = 0; i < warrlength; i ++) {
sheetsetColumnWidth(i, warr[i]);
}
}
// 填充表格头
private void fillTableHeader(HSSFWorkbook workbook, HSSFSheet sheet) {
final String[] contents = new String[] {
"日期",
"车辆",
"燃油种类",
"燃油单价(元/升)",
"加油方式",
"加油量(升)",
"花费(元)",
"备注"
};
int r = 1;
int c = 1;
CellStyle style = workbookcreateCellStyle();
stylesetFillForegroundColor(IndexedColorsYELLOWgetIndex()); // 填充**
stylesetFillPattern(CellStyleSOLID_FOREGROUND); // 填充方式
// 设置border
stylesetBorderLeft(CellStyleBORDER_THIN);
stylesetBorderRight(CellStyleBORDER_THIN);
stylesetBorderTop(CellStyleBORDER_THIN);
stylesetBorderBottom(CellStyleBORDER_THIN);
for (int i = 0; i < contentslength; i ++) {
Cell cell = getCell(sheet, r, c + i);
cellsetCellValue(contents[i]);
cellsetCellStyle(style);
}
}
private void fillTableBody(HSSFWorkbook workbook, HSSFSheet sheet, List<RefuelingRecord> records) {
// 通用style
CellStyle style = workbookcreateCellStyle();
stylesetFillForegroundColor(IndexedColorsWHITEgetIndex()); // 填充白色
stylesetFillPattern(CellStyleSOLID_FOREGROUND); // 填充方式
stylesetBorderLeft(CellStyleBORDER_THIN);
stylesetBorderRight(CellStyleBORDER_THIN);
stylesetBorderTop(CellStyleBORDER_THIN);
stylesetBorderBottom(CellStyleBORDER_THIN);
// 日期style
CellStyle dateStyle = workbookcreateCellStyle();
dateStylesetFillForegroundColor(IndexedColorsWHITEgetIndex()); // 填充白色
dateStylesetFillPattern(CellStyleSOLID_FOREGROUND); // 填充方式
dateStylesetBorderLeft(CellStyleBORDER_THIN);
dateStylesetBorderRight(CellStyleBORDER_THIN);
dateStylesetBorderTop(CellStyleBORDER_THIN);
dateStylesetBorderBottom(CellStyleBORDER_THIN);
dateStylesetDataFormat(HSSFDataFormatgetBuiltinFormat("m/d/yy"));
int r = 2;
int c = 1;
Cell cell = null;
for (int i = 0; i < recordssize(); i ++) {
RefuelingRecord rr = recordsget(i);
// 日期
cell = getCell(sheet, r, c + 0);
if (rrgetDate() != null)
cellsetCellValue(rrgetDate());
cellsetCellStyle(dateStyle);
// 车辆
cell = getCell(sheet, r, c + 1);
if (rrgetVehicle()getNickname() != null)
cellsetCellValue(rrgetVehicle()getNickname());
cellsetCellStyle(style);
// 燃油种类
cell = getCell(sheet, r, c + 2);
if (rrgetGasType() != null) {
String s = null;
switch (rrgetGasType()) {
case _0: s = "0号柴油"; break;
case _93: s = "93号汽油"; break;
case _97: s = "97号汽油"; break;
case _98: s = "98号汽油"; break;
}
cellsetCellValue(s);
}
cellsetCellStyle(style);
// 单价
cell = getCell(sheet, r, c + 3);
if (rrgetPriceOfGas() != null)
cellsetCellValue(rrgetPriceOfGas());
cellsetCellStyle(style);
// 加油方式
cell = getCell(sheet, r, c + 4);
if (rrgetRefuelingType() != null) {
String s = null;
switch (rrgetRefuelingType()) {
case FIXED_CUBAGE:
s = "固定容积"; break;
case FIXED_MONEY:
s = "固定金额"; break;
case FULL:
s = "加满"; break;
}
cellsetCellValue(s);
}
cellsetCellStyle(style);
// 加油量
cell = getCell(sheet, r, c + 5);
if (rrgetCubageOfGas() != null)
cellsetCellValue(rrgetCubageOfGas());
cellsetCellStyle(style);
// 花费
cell = getCell(sheet, r, c + 6);
if (rrgetSumOfMoney() != null)
cellsetCellValue(rrgetSumOfMoney());
cellsetCellStyle(style);
// 备注
cell = getCell(sheet, r, c + 7);
if (rrgetComment() != null)
cellsetCellValue(rrgetComment());
cellsetCellStyle(style);
r ++;
}
}
}
cellsetCellStyle(style);
// 燃油种类
cell = getCell(sheet, r, c + 2);
if (rrgetGasType() != null) {
String s = null;
switch (rrgetGasType()) {
case _0: s = "0号柴油"; break;
case _93: s = "93号汽油"; break;
case _97: s = "97号汽油"; break;
case _98: s = "98号汽油"; break;
}
cellsetCellValue(s);
}
cellsetCellStyle(style);
// 单价
cell = getCell(sheet, r, c + 3);
if (rrgetPriceOfGas() != null)
cellsetCellValue(rrgetPriceOfGas());
cellsetCellStyle(style);
// 加油方式
cell = getCell(sheet, r, c + 4);
if (rrgetRefuelingType() != null) {
String s = null;
switch (rrgetRefuelingType()) {
case FIXED_CUBAGE:
s = "固定容积"; break;
case FIXED_MONEY:
s = "固定金额"; break;
case FULL:
s = "加满"; break;
}
cellsetCellValue(s);
}
cellsetCellStyle(style);
// 加油量
cell = getCell(sheet, r, c + 5);
if (rrgetCubageOfGas() != null)
cellsetCellValue(rrgetCubageOfGas());
cellsetCellStyle(style);
// 花费
cell = getCell(sheet, r, c + 6);
if (rrgetSumOfMoney() != null)
cellsetCellValue(rrgetSumOfMoney());
cellsetCellStyle(style);
// 备注
cell = getCell(sheet, r, c + 7);
if (rrgetComment() != null)
cellsetCellValue(rrgetComment());
cellsetCellStyle(style);
r ++;
}
}
}
四) Controller中返回逻辑视图名 (代码片段)
Java代码
@RequiresUser // 安全框架用元注释
@RequiresRoles({"ROLE_USER"})
@RequestMapping(value = "/list/excel", method = RequestMethodGET)
public String listByExcel(
@DateRangeFormat(pattern = "yyyy-MM-dd") @RequestParam("dateRange") DateRange dateRange,
ModelMap modelMap
)
{
}
// 放入model
modelMapput("dateRange", dateRange);
modelMapput("refuelingRecordList", gasServicefindRefuelingRecordByDateRange(currentUserId, dateRange));
return "refueling-record-list"; // 最终返回逻辑视图名
}
五) 为spring-mvc配置多个视图解析器。
<bean class="orgspringframeworkwebservletviewXmlViewResolver">
<property name="order" value="1" /> <!-- order很重要 -->
<property name="location" value="classpath:/META-INF/viewsxml" />
</bean>
<bean class="orgspringframeworkwebservletviewInternalResourceViewResolver">
<property name="order" value="9999" />
<property name="viewClass" value="orgspringframeworkwebservletviewJstlView" />
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value="jsp"/>
六) 效果图
先来总体说一下我对这个问题的理解,用一句话概括:
数据库是可以控制事务的传播和隔离级别的,Spring在之上又进一步进行了封装,可以在不同的项目、不同的 *** 作中再次对事务的传播行为和隔离级别进行策略控制。
注意:Spring不仅可以控制事务传播行为(PROPAGATION_REQUIRED等),还可以控制事务隔离级别(ISOLATION_READ_UNCOMMITTED等)。
(以下是个人理解,如果有瑕疵请及时指正)
下面我具体解释一下:
为了大家能够更好的理解,先来明确几个知识点:
事务的传播行为:简单来说就是事务是手动提交还是自动提交,事务什么时候开始,什么时候提交。
事务的隔离级别:简单来说,就四个,提交读,提交读,重复读,序列化读。
首先我来描述一下,数据库(mysql)层面上对于事务传播行为和隔离级别的配置和实验方法:
数据库层面(采用命令行):其实mySql命令行很简单,希望实验 *** 作一下:
//连接数据库,我这里是本地,后面是用户名密码,不要打分号,如果指令不行,配置下环境变量,网上有很多。
1 cmd中执行:mysql -hlocalhost -uroot -pmysql
//查看本地数据库事务传播行为是手动提交(0),还是自动提交(1)。
2select @@autocommit;
//如果是0,希望设置为手动提交,这里其实是设置本对话的autocommit,因为如果你再开一个cmd,发现还是没改回来,如果想修改全局的,网上有global方法。
3set @@autocommit=0;
//然后查询本地数据库中的一条记录,我本地数据库为test1;
4use test1;
5select from task where taskid=1;
//同时新开一个窗口cmd,连接数据库,并且修改这条记录,update语句我就不写了,或者直接修改数据库本条记录。
//再次执行select from task where taskid=1;发现值没变。OK因为此时数据库隔离级别为repeatable read 重复读,因为mysql默认的隔离级别是重复读。
//修改数据库隔离级别
6set global transaction isolation level read committed;
//查看一下,可能需要重新连接一下
7select @@tx_isolation;
//这时在执行一下4,5 *** 作,发现值变了,ok。因为已经改变了数据库隔离级别,发生了重复读出不同数据的现象。
(以上 *** 作希望有不明白的上网自学一下,很有用,先把数据库隔离级别弄明白了)
然后再来讲一下,Spring对事务传播行为和隔离级别的二次封装。
因为不同项目可能在一个mysql的不同数据库上,所以可以在项目中配置数据库的传播行为和隔离级别:
关于spring的传播行为(PROPAGATION_REQUIRED、PROPAGATION_REQUIRED等),我《数据库隔离级别(mysql+Spring)与性能分析 》文章中有讲,网上也有很多相关资料,我就不说了。
关于spring的事务隔离级别与数据库的一样,也是那四个,多了一个default,我也不仔细讲了。
下面主要讲一下spring的配置方法:
<property name="transactionAttributes">
<props>
<prop key="save">PROPAGATION_REQUIRED</prop>
<prop key="update">PROPAGATION_REQUIRED</prop>
<prop key="delete">PROPAGATION_REQUIRED</prop>
<prop key="get">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find">PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED</prop>
</props>
就以find为例,可以配置这么配置,前面是控制传播行为,后面是控制事务隔离级别的。那么这时哪怕数据库层面上是重复读,但是还是以这里为准,你会发现在同一个事务中两次查询的结果是不一样的。
最后扫除一个盲区,readonly这个属性,是放在传播行为中的,一般书都这么归类,我也尝试了一下,readonly并不能影响数据库隔离级别,只是配置之后,不允许在事务中对数据库进行修改 *** 作,仅此而已。
1)在applicationContext里面配置Hibernate的dataSource和SessionFactory
2)编写Dao的接口是实现类。如果用到Spring的Hibernate的 *** 作模板(HibernateTemplate)可以继承HibernateDaoSupport,实现类注解成@Repository
3)编写Action,然后注解成@Controller,在Action里面需要用@Autowired注入Dao的实例
4)配置applicationContextxml,加上Spring
MVC的视图处理器
5)编写页面,然后提交请求。搞定!
以上就是关于springmvc下的日志配置全部的内容,包括:springmvc下的日志配置、springmvc excel表格数据导入数据库怎么做、spring mvc 怎么样设置mysql事物隔离级别等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)