在将动态变量实现到SQL语句中时,是否存在比字符串连接更好的方法

时间:2016-05-11 07:43:59

标签: java sql database concatenation string-concatenation

我在Java工作,我有以下方法:

    public ResultSet getRecordsWithinBoundingBox(int spillFarLeftValue, int spillFarRightValue, int spillMostDownwardValue, int spillMostUpwardValue) {
    ResultSet resultSet = null;

    try {
        Statement statement = dbConnection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

        String sql = "SELECT * FROM OTH WHERE (jl<=" + spillMostUpwardValue + " AND (ih>=" + spillFarLeftValue + " AND ih<="
                + spillFarRightValue+ ") OR (il<=" + spillFarRightValue + " AND il>=" + spillFarLeftValue + ")) OR (jh>="
                + spillMostDownwardValue + " AND (ih>=" + spillFarLeftValue + " AND ih<=" + spillFarRightValue + ") OR (il<="
                + spillFarRightValue + " AND il>=" + spillFarLeftValue + ")) OR (il<=" + spillFarLeftValue + " AND ih>="
                + spillFarRightValue + " AND (jl<=" + spillMostUpwardValue + " AND jl>=" + spillMostDownwardValue + ") OR (jh>="
                + spillMostDownwardValue + " AND jh>=" + spillMostUpwardValue + ")) OR (jl<=" + spillMostDownwardValue + " AND jh>="
                + spillMostUpwardValue + " AND (il>=" + spillFarLeftValue + " AND il<=" + spillFarRightValue + ") OR (ih<="
                + spillFarRightValue + " AND ih>=" + spillFarLeftValue + ")) OR (il<=" + spillFarLeftValue + " AND ih>="
                + spillFarRightValue + " AND jl<=" + spillMostDownwardValue + " AND jh>=" + spillMostUpwardValue + ")";

        resultSet = statement.executeQuery(sql);

        statement.close( );
        resultSet.close( );
    } catch (SQLException ex) {
        Logger.getLogger(DatabaseInteractor.class.getName()).log(Level.SEVERE, null, ex);
    }

    return resultSet;
}

正如您所看到的,我目前正在使用一个巨大的字符串从我的数据库中提取数据,并且我被告知这不是最好的解决方案。但遗憾的是,我还没有告诉我应该做什么。但我觉得将SQL语句与我现在的方式放在一起是有风险的,而且我想知道获得相同结果的其他方法。

2 个答案:

答案 0 :(得分:4)

一个很好的选择是使用prepared statements: 示例

sql= "INSERT INTO imt_database.Comment(error_id,user,content) VALUES (?,?,?);";
        try{
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(URL,"root","toor");
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, Error_id);
            ps.setString(2, User);
            ps.setString(3, Content);
            ps.executeUpdate();
        }catch(Exception e)

答案 1 :(得分:0)

学习Java Persistence API也许值得。以下是定义命名查询(带有命名占位符)的代码示例:

@Entity
@Table(name = "passports")
@NamedQueries({
        @NamedQuery(name = "PassportEntity.findAll", query = "SELECT p FROM PassportEntity p"),
        @NamedQuery(name = "PassportEntity.countUniqueAllClients",
                    query = "SELECT count(p) FROM PassportEntity p"
                            + " WHERE p.number = :number"
                            + " AND p.country = :country"),
})
public class PassportEntity implements Serializable {
    @Version
    private int                 version;

    @Id
    @Column(unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int                 id;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false)
    private Date                expires;

    @Column(nullable = false, length = 15)
    private String              number;

    @Column(name = "country", nullable = false, length = 2)
    private String              country;

    // bi-directional many-to-one association to ClientEntity
    @ManyToOne
    @JoinColumn(name = "client_id", nullable = false)
    private ClientEntity        client;

    // Getters & Setters (not needed for version) ...
}

用法示例1:(JEE,例如EJB业务规则,使用应用程序服务器管理的资源注入)

:
@PersistenceContext
private EntityManager em;
:
    public long countPassports(Integer clientId, String ppNumber, CountryEnum ppCountry) {
        return em.createNamedQuery("PassportEntity.countUniqueAllClients", Long.class)
                        .setParameter("number", ppNumber)
                        .setParameter("country", ppCountry.name())
                        .getSingleResult();
    }
:
:

...使用 persistence.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<persistence
    version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
>
    <persistence-unit name="TestDB" transaction-type="JTA">
        <jta-data-source>jdbc/testDataSource</jta-data-source>
        <!-- for Glassfish, create JDBC connection pool as jdbc/testDataSource -->

        <class>module.PassportEntity</class>
        :
    </persistence-unit>
</persistence>

用法示例2:(非EE /独立Java)

:
public class MyApplication {
    private static EntityManagerFactory emf;
    private static EntityManager        em;

    public static void main(String[] args) {
        :
        emf = Persistence.createEntityManagerFactory("TestDB"); // application scoped Entity Manager
        em = emf.createEntityManager();
         :

        try {
            long count = em.createNamedQuery("PassportEntity.countUniqueAllClients", Long.class)
                        .setParameter("number", ppNumber)
                        .setParameter("country", ppCountry.name())
                        .getSingleResult();
        } finally {
            em.close();
            emf.close();
        }
    }
}

...使用 persistence.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
>
    <persistence-unit name="TestDB" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

        <class>module.PassportEntity</class>
        :

        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/mydata" />
            <property name="javax.persistence.jdbc.user" value="***" />
            <property name="javax.persistence.jdbc.password" value="***" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
        </properties>
    </persistence-unit>
</persistence>

暂且不说:我更喜欢为我的所有查询和占位符名称(在定义查询时连接)创建public static String常量,而不是在示例代码中使用的字符串文字,以便编译器/ IDE可以帮助我正确完成它们。在字符串文字中输入拼写错误太容易了,这可能会导致非常令人沮丧和难以发现的错误。容易陷入错误的另一个地方是当您将预定义语句分解为多个符合代码行长的连接字符串时,但在引号中没有空格来分隔文本,所以要经常仔细检查。

在给定已创建表的数据库的情况下,您的IDE(我使用Eclipse)应该能够为您生成大多数Entity类文件和persistence.xml文件。没有多少汗水(但它有助于知道它应该是什么,能够在事后检查或修改或应用一些调整)。

您仍然可以将JPA与预准备语句甚至本机查询一起使用,您只需替换索引占位符(从1开始,如果我没记错的话)。正如其他地方所提到的,字符串连接与实际值(可能来自您的用户界面)是SQL注入攻击的理想之门 - 不要这样做。