More CDI Alignments
In JPA 2.1, EntityListener supports @Inject(some persistence providers are still problematic). JPA 2.2 adds injection support in @AttributeConverter.
Let's reuse the sample codes in my Java EE 7 sample.
The following is the original version for JPA 2.1, which is used to convert a tag list to string in database.
1
@Converter
2
public class ListToStringConveter implements AttributeConverter<List<String>, String> {
3
4
@Override
5
public String convertToDatabaseColumn(List<String> attribute) {
6
if (attribute == null || attribute.isEmpty()) {
7
return "";
8
}
9
return StringUtils.join(attribute, ",");
10
}
11
12
@Override
13
public List<String> convertToEntityAttribute(String dbData) {
14
if (dbData == null || dbData.trim().length() == 0) {
15
return new ArrayList<String>();
16
}
17
18
String[] data = dbData.split(",");
19
return Arrays.asList(data);
20
}
21
}
Copied!
Extract the processing to a standalone CDI bean.
1
@ApplicationScoped
2
public class ConverterUtils {
3
4
public String listToString(List<String> tags) {
5
return join(",", tags);
6
}
7
8
public List stringToList(String str) {
9
return Arrays.asList(str.split(","));
10
}
11
}
Copied!
The new tag converter looks like.
1
@Converter(autoApply = false)
2
public class TagsConverter implements AttributeConverter<List<String>, String> {
3
4
@Inject
5
Logger LOG;
6
7
@Inject
8
ConverterUtils utils;
9
10
@PostConstruct
11
public void postConstruct(){
12
LOG.log(Level.INFO, "calling @PostConstruct");
13
}
14
15
@PreDestroy
16
public void preDestroy(){
17
LOG.log(Level.INFO, "calling @PreDestroy");
18
}
19
20
@Override
21
public String convertToDatabaseColumn(List<String> attribute) {
22
LOG.log(Level.FINEST, "utils injected: {0}", utils != null);
23
if (attribute == null || attribute.isEmpty()) {
24
return "";
25
}
26
return utils.listToString(attribute);
27
}
28
29
@Override
30
public List<String> convertToEntityAttribute(String dbData) {
31
if (dbData == null || dbData.trim().length() == 0) {
32
return Collections.<String>emptyList();
33
}
34
return utils.stringToList(dbData);
35
}
36
37
}
Copied!
Apply it in the entity class.
1
@Entity
2
public class Post implements Serializable {
3
4
static enum Status {
5
DRAFT, PUBLISHED;
6
}
7
8
@Id
9
@GeneratedValue(strategy = AUTO)
10
private Long id;
11
12
private String title;
13
14
private String content;
15
16
@Enumerated(EnumType.STRING)
17
private Status status = Status.DRAFT;
18
19
@Convert(converter = TagsConverter.class)
20
private List<String> tags = new ArrayList<>();
21
22
private LocalDateTime publishedAt;
23
24
private LocalDateTime createdAt;
25
26
private LocalDateTime updatedAt;
27
28
@Version
29
private Long version;
30
31
public Long getId() {
32
return id;
33
}
34
35
public void setId(Long id) {
36
this.id = id;
37
}
38
39
public String getTitle() {
40
return title;
41
}
42
43
public void setTitle(String title) {
44
this.title = title;
45
}
46
47
public String getContent() {
48
return content;
49
}
50
51
public void setContent(String content) {
52
this.content = content;
53
}
54
55
public Status getStatus() {
56
return status;
57
}
58
59
public void setStatus(Status status) {
60
this.status = status;
61
}
62
63
public List<String> getTags() {
64
return tags;
65
}
66
67
public void setTags(List<String> tags) {
68
this.tags = tags;
69
}
70
71
72
public LocalDateTime getPublishedAt() {
73
return publishedAt;
74
}
75
76
public void setPublishedAt(LocalDateTime publishedAt) {
77
this.publishedAt = publishedAt;
78
}
79
80
public LocalDateTime getCreatedAt() {
81
return createdAt;
82
}
83
84
public void setCreatedAt(LocalDateTime createdAt) {
85
this.createdAt = createdAt;
86
}
87
88
public LocalDateTime getUpdatedAt() {
89
return updatedAt;
90
}
91
92
public void setUpdatedAt(LocalDateTime updatedAt) {
93
this.updatedAt = updatedAt;
94
}
95
96
public Long getVersion() {
97
return version;
98
}
99
100
public void setVersion(Long version) {
101
this.version = version;
102
}
103
104
@PrePersist
105
public void beforeSave() {
106
setCreatedAt(LocalDateTime.now());
107
}
108
109
@PreUpdate
110
public void beforeUpdate() {
111
setUpdatedAt(LocalDateTime.now());
112
if (PUBLISHED == this.status) {
113
setPublishedAt(LocalDateTime.now());
114
}
115
}
116
117
@Override
118
public int hashCode() {
119
int hash = 5;
120
hash = 19 * hash + Objects.hashCode(this.title);
121
hash = 19 * hash + Objects.hashCode(this.content);
122
hash = 19 * hash + Objects.hashCode(this.status);
123
return hash;
124
}
125
126
@Override
127
public boolean equals(Object obj) {
128
if (this == obj) {
129
return true;
130
}
131
if (obj == null) {
132
return false;
133
}
134
if (getClass() != obj.getClass()) {
135
return false;
136
}
137
final Post other = (Post) obj;
138
if (!Objects.equals(this.title, other.title)) {
139
return false;
140
}
141
if (!Objects.equals(this.content, other.content)) {
142
return false;
143
}
144
if (this.status != other.status) {
145
return false;
146
}
147
return true;
148
}
149
150
@Override
151
public String toString() {
152
return "Post{"
153
+ "id=" + id
154
+ ", title=" + title
155
+ ", content=" + content
156
+ ", status=" + status
157
+ ", publishedAt=" + publishedAt
158
+ ", createdAt=" + createdAt
159
+ ", updatedAt=" + updatedAt
160
+ '}';
161
}
162
163
}
Copied!
In the above use @Convert to apply it on tags field.
According to JPA 2.2 specification, this should work.
Attribute converter classes in Java EE environments support dependency injection through the Contexts and Dependency Injection API (CDI) [ 7 ] when CDI is enabled[51]. An attribute converter class that makes use of CDI injection may also define lifecycle callback methods annotated with the PostConstruct and PreDestroy annotations. These methods will be invoked after injection has taken place and before the attribute converter instance is destroyed respectively.
NOTE, But it does not work at the moment, see the issue description in details on stackoverflow.
Grab the source codes from my GitHub account, and have a try.
Last modified 10mo ago
Copy link