Ubuntu启动器配置问题

问题

Idea更新,删除原版本文件夹后再启动Idea发现Ubuntu启动器没有应用图标。

解决

在 ~/.local/share/applications文件夹下找到Idea相关配置,如 jetbrains-idea.desktop。

将相应目录改为新应用的目录即可。

这个问题很久没解决,主要是一般都去 /usr/share/applications/下进行配置,不知道当前用户的相关配置在用户目录 ~/.local/share/applications 中。

最后附一个Idea配置,以后其他应用配置启动器可进行参考:

1
2
3
4
5
6
7
8
9
10
[Desktop Entry]
Version=1.0
Type=Application
Name=IntelliJ IDEA Ultimate Edition
Icon=/home/gelu/env/idea-IU-181.4892.42/bin/idea.png
Exec="/home/gelu/env/idea-IU-181.4892.42/bin/idea.sh" %f
Comment=The Drive to Develop
Categories=Development;IDE;
Terminal=false
StartupWMClass=jetbrains-idea

相同类中方法互相调用事务嵌套无效

问题

项目开发中class A中有B、C方法均有自己的事务,在方法B中调用C后发现C的事务不生效。

解决

相同类中方法互相调用事务嵌套无效,因此由外部类D分别调用A中的B、C方法事务生效。

supervisor启动报can't find command问题

问题

supervisor启动java应用不成功,调用supervisorctl status查看进程情况,发现出现
can’t find command ‘java’错误信息。发现可能是环境变量未生效。

解决

修改supervisor配置。

原配置:

1
command=java -jar account.jar

修改后的配置:

1
command=/bin/bash -c 'source "$0" && exec "$@"' /etc/profile java -jar account.jar

修改后的命令先激活环境变量,再执行原命令,此时Java应用启动成功。

Supervisor类似can’t find command的错误均能使用类似方法解决。

Hibernate 启动慢

问题

项目使用PostgreSQL+Hibernate+Spring Boot, 发现启动过慢,有如下日志。

1
2
3
2018-01-18 21:49:31.625 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL95Dialect
2018-01-18 21:50:02.964 [main] INFO o.h.engine.jdbc.env.internal.LobCreatorBuilderImpl - HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException

Disabling contextual LOB creation as createClob() method threw error 费时且没用。

解决

Spring Boot项目中添加如下配置

1
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults: false

添加配置后启动,启动时间可大大缩短。

1
2
3
2018-01-18 21:45:43.852 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
2018-01-18 21:45:43.873 [main] INFO o.h.engine.jdbc.env.internal.LobCreatorBuilderImpl - HHH000422: Disabling contextual LOB creation as connection was null

设置docker时区

docker容器时区修改方法

方法一 使用数据卷volume:

容器启动时加上 -v /etc/localtime:/etc/localtime, 容器就会与宿主机同一时区
若使用编排工具如docker-compose等时可在配置文件指定volume

方法二 直接更新容器配置:

在Dockerfile里指定

1
RUN echo "Asia/Shanghai" > /etc/timezone

Linux时区参考

Spring Boot 端口设置

本文介绍了Spring Boot设置Http端口的几种方式
Spring Boot默认端口为8080

指定端口

在Spring Boot配置文件application.propertites中设置server.port

1
server.port=8888

添加VM配置 -Dserver.port=8888 亦可指定配置

随机端口

1
server.port=0

获取端口号

使用@LocalServerPort注解可在运行时获取程序的Http端口号

1
2
3
4
5
6
7
8
9
10
11
12
13
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {
@Autowired
EmbeddedWebApplicationContext server;
@LocalServerPort
int port;
// ...
}

官方文档详见

Java时间转换-JodaTime转Date,Java8 JavaTime转Date

JodaTime转换

  • org.joda.time.LocalDate转java.util.Date
1
Date date = localDate.toDateTimeAtStartOfDay().toDate();
  • org.joda.time.LocalDateTime转java.util.Date
1
Date date = localDateTime.toDate();

Java8 JavaTime转换

  • java.time.localDate转java.util.Date
1
Date date = Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
  • java.time.LocalDateTime转java.util.Date
1
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
  • java.time.ZonedDateTime转java.util.Date
1
Date date = Date.from(zonedDateTime.toInstant());

Hibernate枚举类型的映射

Hibernate枚举类型的映射

本文介绍一下Hibernate映射枚举类型的几种方式.

首先定义一个枚举.

1
2
3
4
5
6
7
8
9
10
11
12
13
public enum Gender {
male("男"),
female("女");
private String name;
private Gender(String name){
this.name = name;
}
public char getValue() {
return name;
}
}

基本 - 按枚举的ordinal属性映射

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@Column
private String name;
// 没有额外注解 或 添加注解 @Enumerated(EnumType.ORDINAL)
@Column
// @Enumerated(EnumType.ORDINAL) // 可省略
private Gender gender;
// ...
}

按照这种写法, Hibernate会将Enum类型在声明中的顺序即ordinal属性映射成int型存入数据库, 对于枚举Gender来说, male在数据库中存的是 0, 而female是 1.
好处是简单, 缺点是枚举值多时可读性差, 若声明顺序不小心发生变化映射会出错且不易发现.

基本 - 按枚举的name属性映射

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@Column
private String name;
@Column
@Enumerated(EnumType.STRING)
private Gender gender;
// ...
}

使用此方法数据库中存储的是枚举类型的name属性, 即male在数据库中存的就是’male’, female在数据库中存的就是’female’.
好处是同样也比较简单,可读性大于第一种

附EnumType源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Defines mapping for enumerated types. The constants of this
* enumerated type specify how a persistent property or
* field of an enumerated type should be persisted.
*
* @since Java Persistence 1.0
*/
public enum EnumType {
/** Persist enumerated type property or field as an integer. */
ORDINAL,
/** Persist enumerated type property or field as a string. */
STRING
}

灵活映射 - 使用Hibernate自定义类型

自定义类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class GenderType extends AbstractSingleColumnStandardBasicType<Gender> {
public static final GenderType INSTANCE = new GenderType();
public GenderType() {
super(
CharTypeDescriptor.INSTANCE,
GenderJavaTypeDescriptor.INSTANCE
);
}
public String getName() {
return "gender";
}
@Override
protected boolean registerUnderJavaType() {
return true;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class GenderJavaTypeDescriptor extends AbstractTypeDescriptor<Gender> {
public static final GenderJavaTypeDescriptor INSTANCE =
new GenderJavaTypeDescriptor();
protected GenderJavaTypeDescriptor() {
super( Gender.class );
}
public String toString(Gender value) {
return value == null ? null : value.name();
}
public Gender fromString(String string) {
return string == null ? null : Gender.valueOf( string );
}
public <X> X unwrap(Gender value, Class<X> type, WrapperOptions options) {
return CharacterTypeDescriptor.INSTANCE.unwrap(
value == null ? null : value.getValue(),
type,
options
);
}
public <X> Gender wrap(X value, WrapperOptions options) {
return Gender.fromString(
CharacterTypeDescriptor.INSTANCE.wrap( value, options )
);
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@Column
private String name;
@Column
@Type( type = "org.hibernate.userguide.mapping.basic.GenderType" )
public Gender gender;
// ...
}

灵活映射 - 使用AttributeConverter

AttributeConverter可用于各种类型的自定义映射, 当然也包括了枚举类型.
AttributeConverter, 其中 X 是实体属性的类型, Y 是数据库字段的类型.

示例:
定义转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GenderConverter implements AttributeConverter<Gender, String> {
// 将实体属性X转化为Y存储到数据库中
@Override
public String convertToDatabaseColumn(Gender attribute) {
if (attribute == null) {
return null;
}
return attribute.getValue();
}
// 将数据库中的字段Y转化为实体属性X
@Override
public Gender convertToEntityAttribute(String dbData) {
return Gender.fromString(dbData);
}
}

完善枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum Gender {
male("男"),
female("女");
private String value;
private Gender(String value){
this.value = value;
}
public static Gender fromString(String value) {
Gender gender = null;
switch(value): {
case "男":
gender = male;
break;
case "女":
gender = female;
break;
}
return gender;
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column
@Convert(converter = GenderConverter.class)
private Gender gender;
// ...
}

属性转换器需要实现AttributeConverter接口,该接口可以完成实体中数据和数据库中数据的相互转换.

此时在数据库中对应存入的值为’男’,’女’

在实际项目中使用的枚举类型可能又有相似的地方,比如male(“男”), 有一个字符串参数, 需要使用该参数进行映射. 若有若干相似枚举类型, 每个枚举都写属性转换器太过麻烦. 此时可以定义通用接口, 相关枚举类型实现该接口, 然后将转换器AttributeConverter中的X设为该接口. 对应枚举再定义static class去继承通用转换器, 从而简化了代码.

Thymeleaf SAXParseException: The content of elements must consist of well-formed character data or markup

问题

使用Thymeleaf时遇到如下异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Caused by: org.xml.sax.SAXParseException: The content of elements must consist of well-formed character data or markup.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1438)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.startOfMarkup(XMLDocumentFragmentScannerImpl.java:2637)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2735)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:643)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(SAXParserImpl.java:327)
at org.thymeleaf.templateparser.xmlsax.AbstractNonValidatingSAXTemplateParser.doParse(AbstractNonValidatingSAXTemplateParser.java:209)
at org.thymeleaf.templateparser.xmlsax.AbstractNonValidatingSAXTemplateParser.parseTemplateUsingPool(AbstractNonValidatingSAXTemplateParser.java:134)
... 47 common frames omitted

原因

The content of elements must consist of well-formed character data or markup.通常是因为代码不符合XML语法规范,像是元素名称使用数字开头、包含空格等,如:

1
2
<0></0>
< a></a>

查看代码后, 发现在JavaScript代码中有下面的用法

1
if (width < 960) {

Thymeleaf在执行到 < 时会尝试去解析从而出错。

解决办法

使用CDATA避免其中的文本被解析

1
2
3
4
5
<script type="text/javascript">
/*<![CDATA[*/
...
/*]]>*/
</script>