2

I'm working in Java and I've got the following method:

    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;
}

As you can see, I am currently using the a huge string to pull data from my database and I was told that this wasn't the best solution. But sadly I wasn't also told what I should be doing instead. But I feel like it's risky to put together an SQL statement the way I am doing it now, And I'd like to know about alternative ways to get to the same result.

user3776022
  • 217
  • 3
  • 12

2 Answers2

4

A good alternative is using prepared statements : Example

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)
guiguiblitz
  • 407
  • 4
  • 10
  • Would you say this is equal or better as Parameterized SQL queries? – user3776022 May 11 '16 at 10:17
  • 1
    In my opinion, they are exactly the same thing, but i am not a expert. – guiguiblitz May 11 '16 at 11:30
  • I believe that they both prevent any kind of SQL injection, which is good enouth for me – guiguiblitz May 11 '16 at 11:31
  • I've figured out to make all but one part work within my own program. It's when I try to change the table name into a '?' question mark inside the SQL string, like: "Select * FROM ?" and then add ps.setString(1, "OTH"); I'm getting a syntax error at "?". Would you know why? – user3776022 May 11 '16 at 12:47
  • 1
    Oh, this is not possible, you can only specify values with the ? . For the table part, you will have to use concatenation/placeholder. http://stackoverflow.com/questions/11312155/how-to-use-a-tablename-variable-for-a-java-prepared-statement-insert – guiguiblitz May 11 '16 at 12:54
0

It might also be worthwhile to learn the Java Persistence API. Here is a code sample defining named queries (with named placeholders):

@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) ...
}

Usage example 1: (JEE, e.g. EJB business rule, using injection of application server-managed resources)

:
@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();
    }
:
:

... with the persistence.xml file:

<?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>

Usage example 2: (Non-EE/standalone 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();
        }
    }
}

... with the persistence.xml file:

<?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>

As an aside: I prefer to create public static String constants for all my query and placeholder names (concatenated in when defining the query), instead of the string literals as used in the example code, so that the compiler/IDE can help me get them right. It is just too easy making a typo in a string literal, which may lead to very frustrating and hard-to-find bugs. Another place where bugs creep in easily is when you break your predefined statement into a number of concatenated strings that fit in the line length of your code, but fail to have space inside the quotes to separate the text, so always double-check.

Your IDE (I use Eclipse) should be able to generate most of the Entity class files and the persistence.xml file for you, given a database that already has the tables created. Not much sweat there (but it helps to know what it should be, to be able to check or modify afterwards or apply some tweaks).

You can still use JPA with prepared statements or even native queries, where you simply replace indexed placeholders (starting from :1, if I remember correctly). As mentioned elsewhere, string concatenation with actual values (which may come from your user interface) are an ideal door for SQL injection attacks - don't do it.

frIT
  • 3,213
  • 1
  • 18
  • 22