截断要保存的字符串?

时间:2015-04-20 17:06:45

标签: java hibernate jpa

我正在使用JPA。如果字符串长度超过列大小,我需要在保存之前修剪字符串。我有以下代码。

可以根据相应字段的setter中的JPA注释截断字符串:

public void setX(String x) {
    try {
        int size = getClass().getDeclaredField("x").getAnnotation(Column.class).length();
        int inLength = x.length();
        if (inLength>size)
        {
            x = x.substring(0, size);
        }
    } catch (NoSuchFieldException ex) {
    } catch (SecurityException ex) {
    }
    this.x = x;
}

注释本身应如下所示:

@Column(name = "x", length=100)
private String x;

现在我的问题是,我想要许多实体中许多字段的逻辑。如何编写/使上面的逻辑成为通用的,以避免所有字段的所有实体中出现重复的代码。

2 个答案:

答案 0 :(得分:0)

我看到两个选项,这两个选项都不会像你想的那样动手。

  1. 在库org.Reflections中使用方面。我认为这必须是编译时使用aspectj,因为你必须使用Reflection来获取具有注释的所有字段,然后找出哪些setter属于这些字段,然后应用这些字段的方面
  2. 使用静态方法,在每个setter中使用内部反射和一行代码。
  3. 我尝试了第一条路径并开始变得过于复杂,所以我尝试了#2并使用了this question中的内容。

    public static String truncate(String string)
    {
        try
        {
            final StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2];
            final Class<?> clazz = Class.forName(stackTraceElement.getClassName());
            final BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            final String methodName = stackTraceElement.getMethodName();
            final PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
            for(final PropertyDescriptor descriptor : props)
            {
                if(descriptor.getWriteMethod() != null)
                {
                    if(methodName.equals(descriptor.getWriteMethod().getName()))
                    {
                        System.out.println(descriptor.getDisplayName());
                        final Column annotation =
                                clazz.getDeclaredField(descriptor.getDisplayName()).getAnnotation(Column.class);
                        if(annotation != null)
                        {
                            final int size = annotation.length();
                            if(size == 255)
                            {
                                // Highly likely we hit the default value, which means nothing was specified so just
                                // move along
                            }
                            else if(string.length() > size)
                            {
                                string = string.substring(0, size);
                            }
                        }
    
                        break;
                    }
                }
            }
        }
        catch(final IntrospectionException e)
        {
        }
        catch(final Exception e)
        {
        }
    
        return string;
    }
    

    您有基本的列定义

    @Column(name = "personTitle", length = 5)
    private String personTitle;
    

    然后在setter中

    public void setPersonTitle(final String personTitle)
    {
        this.personTitle = JpaStringTruncator.truncate(personTitle);
    }
    

    这假设您的setter正在调用静态方法,因此您只需要回溯StackTrace中的一些元素。

答案 1 :(得分:0)

我使用的方法是简单地在所有实体类中定义保存每个String字段最大大小的int变量,并在设置器中使用这些变量来修剪字符串(如果它们超过了大小限制)。

由于我是使用Netbeans的Entity classes from database向导动态生成实体类的,所以我需要一种自动化的方式来生成int变量,如下所示:

public static final int ID_NUMBER_SIZE = 16;
public static final int WEB_SITE_NAME_SIZE = 100;
public static final int WEB_URL_SIZE = 50;

因此,我编写了以下python脚本,该脚本解析给定目录中的所有*.java文件,并使用正则表达式匹配包含@Size约束的字段定义。只需在命令行py entities_size.py上调用脚本并刷新IDE中的显示(在Eclipse中为 F5 ),即可在源文件外部对其进行修改时运行该脚本。如果您首先要测试脚本,则可以注释掉最后一行,将更改写入文件。

import os,glob,re
from pathlib import Path

path = 'C:\\users\\pierre\\git\\myProject\\backend\\src\\main\\java\\ca\\qc\\myDomain\\myApp\\entity'

# For each *.java file in the path above
for filename in glob.glob(os.path.join(path, '*.java')):
    with open(filename, 'r') as f:
        # Read each file
        contents = f.read()
        # Find all String variables' name and size with a regex
        varsAndSizesTuples = re.findall(r'@Size\(max\s*=\s*(\d+)[\s\S]*?(?=@Column\(name\s*=\s*\"(\w*)\")', contents, re.MULTILINE)
        newString = "\n"
        # Iterate over each variable and size to create the java code for a new int variable 
        # and accumulate those declarations in the newString variable
        for tuple in varsAndSizesTuples:
            newString = newString + "\tpublic static final int " + str(tuple[1]) + "_SIZE = " + str(tuple[0]) + ";\n"
        print(filename + "\n" + newString + "\n")
        # Finds the position of the class declaration with a regex
        index = re.compile(r"public class.*{[^}]").search(contents).end()
        # Inserts the new variables declaration right after the class declaration
        contents = contents[:index] + newString + contents[index:]
    # Write the changes to the file
    with open(filename, 'w') as f: f.write(contents)        

第一个正则表达式@Size\(max\s*=\s*(\d+)[\s\S]*?(?=@Column\(name\s*=\s*\"(\w*)\")用于提取字段名称及其大小限制(在regex101上进行测试,请参见下文),而第二个正则表达式public class.*{[^}]用于查找字段名称和大小限制。 public class声明以在其后插入新变量声明。

enter image description here

完成此操作后,我编写了以下静态方法以返回修剪后的字符串(如果需要)。请注意,为简单起见,该方法不会测试null或其他无效参数。

public static String trim(String string, int limit) {
    return string.substring(0, Math.min(string.length(), limit) - 1);
}

需要在每个设置器上调用该方法,并使用要验证的字符串及其大小限制:

myEntity.setIdNumber(RefTblInit.trim(stringToPossiblyTrim, MyEntity.ID_NUMBER_SIZE));

这样,我确保我的代码不会因为设置器的参数超出其大小限制而中断。