给定类似邮政编码的分层代码/名称架构。
例如:
代码= 101010
代码:
- 100000第1级代码(10 ....)
- 101000二级代码(..10 ..)
- 101010 3级代码(.... 10)
姓名(简称)
- 100000 - A
- 101000 - a
- 101010 - 我
名称(FullQualifiedName)
- 100000 -
A
- 101000 -
A->a
- 101010 -
A-a->i
修改
我想跟随代码(JPA伪代码),但不能。
@Entity
public class CodeName{
// ....
String code; // 100101 levels = {100000, 100100, 100101}
String name; //
@HowToMapDirectedToNameOfCode('100000') // @SecondTable ?
String name1;
@HowToMapDirectedToNameOfCode('100100')
String name2;
@HowToMapDirectedToNameOfCode('100101')
String name3;
String getFullQualifiedName(){
return String.format("%s->%s->%s", name1, name2, name3);
}
// getter and setter
}
但是在原生SQL中它相对容易:
SELECT (select p1.name from codename p1 where p1.code= concat( substring(p.code,1,2), "0000") ) province,
(select p2.name from codename p2 where p2.code= concat( substring(p.code,1,4), "00") ) city,
(select p3.name from codename p3 where p3.code=p.code) area
FROM codename p WHERE p.code = '100101';
因此,我将其实现为以下代码段。
@Entity
public class CodeName{
// ....
String code; // 100000, 101000, 100101
String name; // province, city , area
@Transient
String name1; // mapping directly?
@Transient
String name2; // mapping directly?
@Transient
String name3; // mapping directly?
String getFullQualifiedName(){
return String.format("%s->%s->%s", name1, name2, name3);
}
// getter and setter
}
public interface CodeNameRepository extends CrudRepository<CodeName, Long>, CodeNameRepositoryCustom {
@Query(" FROM CodeName p " +
" WHERE p.code = CONCAT(SUBSTRING(?1, 1, 2), '0000') " +
" OR p.code = CONCAT(SUBSTRING(?1, 1, 4), '00') " +
" OR p.code = ?1")
List<CodeName> findAllLevelsByCode(String code);
}
@Component
public class CodeNameRepositoryImpl implements CodeNameRepositoryCustom {
@Autowired
private CodeNameRepository codeNameRepository ;
@Override
public CodeName CodeNamefindFullQualifiedNameByCode(String code) {
List<CodeName> codeNames= codeNameRepository .findAllLevelsByCode(code);
CodeName codeName;
// extra name1, name2, name3 from list,
// fill code, name, name1, name2, name3 to codeName and
return codeName;
}
}
但它有很多限制。
CodeName
作为其子项,无论codeName
有多深,我都必须扩展到codeName
并使用FQN重新加载。我们可以直接通过JPA映射所有@Transient名称吗?
答案 0 :(得分:2)
您可以在技术上为您的代码存储库实体建模,如下所示:
public class CodeName {
@Id
@GeneratedValue(GenerationStrategy.AUTO)
@Column
private Long id;
@ManyToOne
private CodeName parent;
@OneToMany(mappedBy = "parent")
private List<CodeName> children;
@Column
private String name;
@Transient
public String getFullyQualifiedName() {
List<String> names = new ArrayList<>();
names.add(name);
CodeName theParent = parent;
while(theParent != null) {
names.add(theParent.getName());
theParent = theParent.parent;
}
Collections.reverse(names);
return StringUtils.join(names, "->");
}
}
因为父关系将被映射为@ManyToOne
,所以它们将被映射为CodeName
,因此您基本上可以从任何子getFullyQualifiedName
实体开始,并将它的父/子关系遍历到根。这基本上允许@Column private String fullyQualifiedName
方法在运行时为您构建名称。
如果执行此操作时性能成为问题,您可以随时通过添加<meta name="viewport" content="width=device-width; initial-scale=0.4;" />
来确定实体中的名称,并确保在创建代码时插入字段。然后我可以删除我添加到实体中的瞬态方法,因为您在数据插入时缓存了名称。
答案 1 :(得分:1)
可以编写一个JPQL,它等同于您的SQL查询。唯一棘手的部分是将嵌套选择重写为交叉连接,因为JPA不支持嵌套选择,您需要加入不相关的实体。另一方面,JPQL支持函数CONCAT
和SUBSTRING
,方式与SQL相同。请参阅以下JPQL查询,该查询应该将结果作为SQL查询提供给您:
SELECT p1.name // province
, p2.name // city
, p.name // area
FROM CodeName p, CodeName p1, CodeName p2
WHERE p.code = '100101'
AND p1.code = concat( substring(p.code,1,2), "0000")
AND p2.code= concat( substring(p.code,1,4), "00")
上述查询将在一行中为您提供3个值,这些值无法映射到单个实体。因此,查询的结果将是Object []数组的列表。您还可以将原始实体添加到select子句中:SELECT p1.name, p2.name, p.name, p FROM ...
。这样,您可以稍后处理结果列表并将前三个值分配到实体的瞬态字段中:
Object[] rows = query.getResultList();
for (Object row : rows) {
CodeName c = (CodeName)row[3];
c.setName1((String)row[0]);
c.setName2((String)row[1]);
c.setName3((String)row[2]);
}