How to query a bank from a thread?

Asked

Viewed 522 times

-3

A teacher asked to develop a project (a CRUD of cars). I’ve done the part of inserting, removing, listing and changing.

But the listing part should be done by a thread (teacher requirement), in which she should list a car every 20 seconds and show in a JTextArea. My question is how to make the thread do that.

Follows the code:

Class of Thread to list. Note: I haven’t done anything in this class yet:

public class ThreadListar implements Runnable{


    private int tempo;


    public ThreadListar(int tempo) {
        this.tempo=tempo;

        Thread t1=new Thread(this);
        t1.start();

    }



    @Override
    public void run() {

        try {
            Thread.sleep(tempo);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

Class CarroDAO with the list method:

@Override
public ArrayList<Carro> listar() {

    ArrayList<Carro> carros= new ArrayList<Carro>();
    String sql="select * from carro";

try(Connection con= new ConnectionFactory().getConnection()){

    PreparedStatement ps= con.prepareStatement(sql);

    ResultSet rs= null;

    rs=ps.executeQuery();

    while(rs.next()) {

        Carro c= new Carro();

        c.setId(rs.getInt("id"));
        c.setMarca(rs.getString("marca"));
        c.setModelo(rs.getString("modelo"));
        c.setCor(rs.getString("cor"));
        c.setPlaca(rs.getString("placa"));

        carros.add(c);
    }   

    ps.close();
    rs.close();

}catch(SQLException e){

    JOptionPane.showMessageDialog(null, "Erro ao realziar consulta:"+e, "ERROR", JOptionPane.ERROR_MESSAGE);
    throw new RuntimeException(e);

}
    return carros;
}

Class Tela (swing) with the button and action of listing and setting the data within the JTextArea:

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                Tela frame = new Tela();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });

}

public Tela() {


JButton btnListar = new JButton("Listar");
    btnListar.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {



            CarroDAO dao1 = new CarroDAO();

            ArrayList carros= dao1.listar();

            for(Object o: carros) {

                Carro c= (Carro) o;


                textArea.append(String.valueOf("ID: "+c.getId()+"\n"));
                textArea.append("Marca: "+c.getMarca()+"\n");
                textArea.append("Modelo: "+c.getModelo()+"\n");
                textArea.append("Cor: "+c.getCor()+"\n");
                textArea.append("Placa: "+c.getPlaca()+"\n"+"\n");
                textArea.append("=================");
            }

        }
    });


    btnListar.setBounds(234, 233, 89, 23);
    contentPane.add(btnListar);
    }
}
}
  • And that’s not clear e listar um carro a cada 20 segundos , and when the list is over, what will happen? Will this list be done after the bank search? Need more details on how this should work.

  • Oh so that’s why you didn’t answer straight ? ,why didn’t you say so? We were supposed to ask these questions about the functionalities in the beginning, at least we wouldn’t waste time "arguing". What I want is: when the user clicks on the list button the thread performs the list method (which is a select) of the Carrodao class, and returns(lists) all the cars registered in my bank ,only one every 20 seconds

  • You don’t need a thread for that. Unless Thread is required, you can do it with a Timer. And please, let’s just force the question,

  • My teacher demanded that you do it with threads, it’s not a question of "no need for thread", but that it’s mandatory to use the thread in this project

2 answers

1

Due to the need to use a separate thread and a timer, the class can be used ScheduledExecutorService, which serves to schedule tasks in the JVM, and run them repeatedly (if necessary) in a thread pool parallel to the current one. You can’t forget that swing is not Thread-Safe, therefore, we must keep the change of the component in the same thread responsible for the graphical interface, the Event-Dispatch-thread.

In the example below, I create only one thread through the method newScheduledThreadPool() and I program the execution with the method scheduleAtFixedRate(), which receives as parameters the task that will be executed, the initial delay for it to begin, the time interval between each execution and the unit of time.

Finally, within the task, I used a counter to display the values of the list with each repetition of the task, and also so that it can be finalized when it reaches the end of the list. The variable must be a class field, otherwise it will not be possible to modify it.

...

//o contador deverá ser uma variável de classe
private volatile int index = 0;

...

btnListar.addActionListener(e -> {

    CarroDAO dao1 = new CarroDAO();

    List<Carro> carros = dao1.listar();

    ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);

    Runnable run = () -> {

        if (index < carros.size()) {
            SwingUtilities.invokeLater(() -> {
                textArea.append(String.valueOf("ID: " + carros.get(index).getId() + "\n"));
                textArea.append("Marca: " + carros.get(index).getMarca() + "\n");
                textArea.append("Modelo: " + carros.get(index).getModelo() + "\n");
                textArea.append("Cor: " + carros.get(index).getCor() + "\n");
                textArea.append("Placa: " + carros.get(index).getMarca() + "\n" + "\n");
                textArea.append("=================");
                index++;
            });
        } else {
            index = 0;
            ses.shutdown();
        }

    };
    ses.scheduleAtFixedRate(run, 0, 20, TimeUnit.SECONDS);

});
  • The only detail is that you’re not exactly using a thread pool since the pool created has only one thread (Executors.newScheduledThreadPool(1)). Despite that, I think your answer should be almost correct and satisfactory: The listing should happen within the Runnable!

  • You could use a variable of the type AtomicInteger instead of an instance variable volatile. By the way, the comment says that she is classy, but she is not. I preferred to use a Iterator instead.

  • @Victorstafusa the fact of the answer is "complete" or is not point of view. From mine, which I have had an effort to create, research and test before, it is more than complete and functional. As for suggestions, I focused only on the question problem and the author’s explanation in the comments, within what I know.

1

To answer by Articuno is on the right track. However, separating what goes in the EDT from what goes in the thread is easier using the SwingWorker, including taking care to query the DAO outside the EDT in an auxiliary thread:

public class Tela extends JFrame {

    private final JButton btnListar;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Tela().setVisible(true));
    }

    public Tela() {
        this.btnListar = new JButton("Listar");
        btnListar.addActionListener(e -> realizarListagem());
        btnListar.setBounds(234, 233, 89, 23);
        contentPane.add(btnListar);
    }

    private void realizarListagem() {
        btnListar.setEnabled(false);
        SwingWorker<Void, Carro> worker = new SwingWorker<Void, Carro>() {
            @Override
            protected Void doInBackground() {
                ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);

                // Usamos o Iterator para acessar um carro por vez.
                Iterator<Carro> it = new CarroDAO().listar().iterator();

                Runnable run = () -> {
                    if (isCancelled() || !it.hasNext()) {
                        ses.shutdown();
                    } else {
                        // Pega o próximo carro e publica.
                        publish(it.next());
                    }
                };

                // Executa o Runnable uma vez a cada 20 segundos.
                ses.scheduleAtFixedRate(run, 0, 20, TimeUnit.SECONDS);

                // Espera a lista de carros terminar.
                try {
                    ses.awaitTermination(99999, TimeUnit.DAYS);
                } catch (InterruptedException e) {
                    // Ignora a exceção e deixa o SwingWorker terminar graciosamente.
                }
                return null;
            }

            @Override
            protected void process(List<Carro> carros) {
                // Isto daqui NÃO deve ser uma lista com todos os carros.
                // Na verdade, espera-se que este método seja chamado com
                // um carro por vez.
                for (Carro c : carros) {
                    textArea.append(String.valueOf("ID: " + c.getId() + "\n"));
                    textArea.append("Marca: " + c.getMarca() + "\n");
                    textArea.append("Modelo: " + c.getModelo() + "\n");
                    textArea.append("Cor: " + c.getCor() + "\n");
                    textArea.append("Placa: " + c.getMarca() + "\n\n");
                    textArea.append("=================");
                }
            }

            @Override
            protected void done() {
                btnListar.setEnabled(true);
            }
        };
        worker.execute();
    }
}

Your DAO also has a problem: If you are already using the Try-with-Resources, then there should be no sense in calling it the method close() explicitly. Here’s how your revised code looks:

private static final String INSERT_SQL = "SELECT * FROM Carro";

@Override
public List<Carro> listar() {

    List<Carro> carros = new ArrayList<>();

    try (
        Connection con = new ConnectionFactory().getConnection();
        PreparedStatement ps = con.prepareStatement(SQL);
        ResultSet rs = ps.executeQuery();
    ) {
        while (rs.next()) {
            Carro c = new Carro();
            c.setId(rs.getInt("id"));
            c.setMarca(rs.getString("marca"));
            c.setModelo(rs.getString("modelo"));
            c.setCor(rs.getString("cor"));
            c.setPlaca(rs.getString("placa"));
            carros.add(c);
        }
    } catch (SQLException e) {
        JOptionPane.showMessageDialog(null, "Erro ao realziar consulta: " + e, "ERROR", JOptionPane.ERROR_MESSAGE);
        throw new RuntimeException(e);
    }
    return carros;
}

And note that I changed the return from ArrayList<Carro> for List<Carro>. There’s a programming principle that says "encode for an interface, not for an implementation". All right, List is an interface and ArrayList is an implementation.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.