Malmö högskola Teknik och samhälle 2007/2008 Laboration 24 – Databasen MySQL och java Avsikten med denna laboration är att du ska hämta information ur en eller flera tabeller och visa resultatet i en JTable-komponent. Du ska ändra innehållet i tabellen och se till att kolumn-bredderna är bra. För att kunna göra detta måste du ha tillgång till en databas och kunna kontakta databasen. På Malmö högskola står en MySQL-server som du kan koppla upp dig från en nätansluten dator. För att detta ska vara möjligt måste du: 1. Hämta en databas-driver från www.mysql.com. 2. Använda samma inloggningsuppgifter som på laboration 23. 1. Hämta databas-driver till MySQL På mysql:s hemsida kan du hämta en driver till en MySQL-databas. Drivern innehåller alla klasser som behövs för att du på ett ganska enkelt sätt ska kunna jobba mot en databas. Adressen till hemsidan är www.mysql.com och du ska sedan klicka på Developer Zone - Downloads - Connectors - Connector/J. Du ska nu vara på sidan http://dev.mysql.com/downloads/connector/j/5.1.html. Hämta hem lämplig arkiv-fil och packa upp den på din dator. Filen mysql-connector-java-5.1.5-bin.jar placerar du t.ex. i C:\java (M:\java på MAH). Sedan måste NetBeans hittar jar-filen. Du ska högerklicka på projektet, välja Properties och sedan klicka på Libraries. Sedan klickar du på knappen Add JAR/Folder och letar upp mysql-...-bin.jar. Markera filen och klicka sedan på OK. 2. Inloggningsuppgifter När du kopplar upp dig mot databasen så måste du ange inloggningsuppgifter. Du använder samma som på laboration 23. Testa databas-drivern Med programmet MysqlDB i F24 kan du testa om du kan koppla upp mot databasen. Om uppkoppliongen går som tänkt så kommer du se innehållet i tabellen Person i databasen test. Testa även att du kan koppla upp mot din egen databas. Uppgift 1 – Hämta kolumnnamnen i en resultatmängd När du ska visa resultatet av en databas-sökning är det trevligt att presentera namn på kolumnerna överst. Om du vill använda kolumnnamnen som kommer från databasen så går dessa bra att hämta. Du ska i klassen DBMethods skriva metoden public static String[] getHeaders(ResultSet rs) throws SQLException Följande har du till din hjälp: • Du kan erhålla ett objekt av typen ResultSetMetaData med metoden rs.getMetaData(). • I ResultSetMetaData-objektet finns följande metoder: * getColumnCount() – returnerar antalet kolumner i resultatmängden. Denna uppgift är nödvändig om du ska skapa en String-array med korrekt antal element. * getColumnLabel(int columnIndex) – returnerar namnet på kolumnen med angivet index. Kolumnerna numreras från 1 upp till getColumnCount(). Samtliga metoder ovan kan kasta SQLException. Du kan även titta hur utskriften sker i MysqlDB.java. Testa din metod genom att koppla upp mot test-databasen, hämta alla kolumner i person-tabellen och skriv ut kolumn-namnen. Du ska då få en utskrift som innehåller: pnr, namn, alder, langd DA129A, Programmering 1 1 Malmö högskola Teknik och samhälle 2007/2008 Uppgift 2 – Hämta datainnehållet i en resultatmängd Det väsentligaste när man ska visa resultatet av en databassökning är ju naturligtvis att visa den data som sökningen givit. Denna information kan lagras i en två-dimensionell array. Men vilken typ av array ska man välja? Det är ju oftast olika typer av data i resultatmängden. Metoden getObject i objektet ResultSet löser detta problem. Till varje typ av datainnehåll returneras ett objekt, t.ex.. Datatyp INT DECIMAL DOUBLE BOOLEAN DATE STRING Objecttyp Integer BigDecimal Double Boolean Date String Sedan kan man skriva ut dessa på ett snyggt sätt med hjälp av deras toString-metod. Du ska i klassen DBMethods skriva metoden public static Object[][] getContent(ResultSet rs) throws SQLException vilken ska lagra innehållet i resultatmängden i en två-dimensionell array av typen Obejct[ ][ ]. I denna metod kan du använda dig av följande • Du kan erhålla ett objekt av typen ResultSetMetaData med metoden rs.getMetaData(). Och sedan ta reda på antalet kolumner på samma sätt som i uppgift 1. • Ta reda på antalet rader på följande sätt: Flytta rad-pekaren till sista raden: rs.last(); Lagra sista radnumret i rows: rows = rs.getRow(); • Skapa en tvådimensionell array: • Du kan föra över data från resultatmängden till arrayen i en nästlad loop där du hämtar informationen i en speciell rad och kolumn så här: Object[][] data = new Obejct[rows][cols]; for(int row = 0; row<rows; row++) { rs.absolute(row + 1); // Flytta till rätt rad i resultatmängden for(int col = 0; col<cols; col++) { data[row][col] = rs.getObject(col + 1); } } Samtliga metoder ovan kan kasta SQLException. Du kan även titta hur utskriften sker i MysqlDB.java. Testa din metod genom att koppla upp mot test-databasen, hämta alla kolumner i person-tabellen och skriv ut kolumn-namnen. Du ska då få en utskrift som innehåller: 631008-1111, 600807-2222, 620101-3333, 731201-4444, Oskar AL, 41, 182 Eva Ask, 44, 163 Bo Ek, 42, 174 Gudrun Bok, 31, 172 DA129A, Programmering 1 2 Malmö högskola Teknik och samhälle 2007/2008 Uppgift 3 – Visa i tabell, version 1 Nu kan du visa informationen i en JTabel-komponent. Det finns en konstruktor i JTable som ser ut så här: public JTable(Object[][] rowData, Object[] columnNames) Den passar perfekt för att visa en av våra tabeller. Men man måste placera JTable-komponenten i en JScrollPane för att tabellhuvudet ska synas. private JTable table; private JScrollPane scrollPane; : Container c = getContentPane(); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(400,300); try { MysqlDB.kopplaUpp(); ResultSet rs = MysqlDB.statement.executeQuery("SELECT * FROM Person"); String[] headers = DBMethods.getHeaders(rs); Object[][] content = DBMethods.getContent(rs); table = new JTable(content, headers); scrollPane = new JScrollPane(table); c.add(scrollPane, BorderLayout.CENTER); } catch(SQLException e) {} setVisible(true); Testa genom att placera en tabell i ett fönster. Uppgift 4 – Sätta kolumnbredder i tabell Ett problem med tabellen är att alla kolumner får samma bredd. Och detta är oftast inte önskavärt. Det går bra att sätta bredden på kolumner i en tabell. Man gör så här: För varje tabell finns objektet TableColumnModel. Med hjälp av detta objekt kan man bl.a. ange bredden på kolumnerna. Tabellens TableColumnModel-objekt erhåller man genom anrop till getColumnModel: TableColumnModel columnModel = table.getColumnModel(); Sedan kan man sätta bredden på önskad kolumn genom anropet columnModel.getColumn(index).setPreferredWidth(bredd); Naturligtvis måste index vara i intervallet [1 , antalKolumnerITabellen]. Kolumnbredden är relativ om man inte anger något annat. Med detta menas att hela tabellens bredd fylls ut och kolumnbredderna anpassas till utrymmet. Vill man ange absoluta kolumnbredder måste man ändra tabellens beteende med anropet table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); Du ska i klassen DBMethods skriva metoden public static void setColumnWidth(JTable table, int[] colWidth) vilken tar en JTable-komponent och en int-array som input och som använder värdena i int-arrayen för att sätta kolumnbredderna i tabellen. Första elementet i colWidth ger bredden på första kolumnen osv. När du skriver metoden är det bäst att kontrollera att antalet element i colWidth = = antalet kolumner i tabellen, annars blir det kanske körfel. DA129A, Programmering 1 3 Malmö högskola Teknik och samhälle 2007/2008 Uppgift 5 – Avläsa markerade rader i tabellen När en tabell visar sig så kan användaren markera en eller flera rader i tabellen. Om du vill ta reda på vilka rader som är markerade i en tabell kan du anropa metoden getSelectedRows i tabellen: int[] valdaRader = table.getSelectedRows(); Efter anropet innehåller valdaRader numret på de rader som var markerade när anropet gjordes. Radnumreringen börjar med 0 och detta passar bra om man ska hämta information ur tabellen med metoden getValueAt(int rad, int kol). Värdet i rad 1, kolumn 1 hämtas med anropet Object data = table.getValueAt(0, 0); Utöka programmet i uppgift 3 med en knapp. När användaren klickar på knappen ska numret på markerade rader i tabellen skrivas ut i output-fönstret. Uppgift 6 – Ändra datan i befintlig tabell Tyvärr går det inte att ändra innehållet i en tabell när man använder konstruktorn i Uppgift 3. För att detta ska gå bra måste man använda konstruktorn public JTable(TableModel tm) TableModel-objektet som levereras vid konstruktionen måste implementera gränssnittet TableModel. En klass som gör detta skulle vi kunna konstruera (9 metoder att implementera korrekt). Men det finns en färdig klass i java som tillfredsställer våra behov, nämligen DefaultTableModel (i paketet javax.swing.table). Denna klass innehåller bl.a. konstruktorn: public DefaultTableModel(Object[][] data, Object[] columnNames) Den innehåller också en metod för att ändra befintliga data. Ändrar vi tabelldata med denna metod så ändras tabellinnehållet automatiskt: public void setDataVector(Object[][] dataVector, Object[] columnIdentifiers) Nu ska du modifiera programmet i uppgift 3 så att ett objekt av typen DefaultTableModel används för att ge tabellen innehåll. private JTable table; private JScrollPane scrollPane; private DefaultTableModel tableModel : Container c = getContentPane(); String[] headers = DBMethods.getHeaders(rs); Object[][] content = DBMethods.getContent(rs); tableModel = new DefaultTableModel(content, headers); table = new JTable(tableModel); scrollPane = new JScrollPane(table); c.add(scrollPane, BorderLayout.CENTER); Passa på att skriva metoden setTableContent i fönsterklassen: public void setTableContent(Object[][] content, Object[] headers) { tableModel.setDataVector(content, headers); } Lägg in en knapp i fönsterklassen som vid klick 1. Hämtar en ny resultatmängd från databasen, t.ex. innehållet i Telefon. 2. Hämtar resultatmängdens kolumnnamn till en String-array 3. Hämtar resultatmängdens data till en Object[ ][ ]. 4. Anropar metoden setTableContent (se ovan). 5. Anropar metoden DBMethods.setColumnWidth för att ge kolumnerna vettiga bredder. DA129A, Programmering 1 4 Malmö högskola Teknik och samhälle 2007/2008 DBMethods.java import java.sql.*; import javax.swing.*; import javax.swing.table.*; public class DBMethods { public static String[] getHeaders(ResultSet rs) throws SQLException { ResultSetMetaData meta; String[] headers; meta = rs.getMetaData(); headers = new String[meta.getColumnCount()]; for(int i=0; i<headers.length; i++) { headers[i] = meta.getColumnLabel(i+1); } return headers; } public static Object[][] getContent(ResultSet rs) throws SQLException { ResultSetMetaData rsmt; Object[][] content; int rows, cols; rsmt = rs.getMetaData(); rs.last(); rows = rs.getRow(); cols = rsmt.getColumnCount(); content = new Object[rows][cols]; for(int row = 0; row<rows; row++) { rs.absolute(row + 1); // Flytta till rätt rad i resultatmängden for(int col = 0; col<cols; col++) { content [row][col] = rs.getObject(col + 1); } } return content; } public static void setColumnWidth(JTable table, int[] colWidth) { TableColumnModel columnModel = table.getColumnModel(); int count = Math.min(table.getColumnCount(),colWidth.length); for(int i=0; i<count; i++) { columnModel.getColumn(i).setPreferredWidth(colWidth[i]); } } } DA129A, Programmering 1 5 Malmö högskola Teknik och samhälle 2007/2008 Uppgift 3 + Uppgift 5 + Uppgift 6 import import import import import javax.swing.*; javax.swing.table.*; java.awt.*; java.awt.event.*; java.sql.*; public class Uppgift3 extends JFrame { private JTable table; private JScrollPane scrollPane; private DefaultTableModel tableModel; // // // // // Uppg 6 public Uppgift3() { Container c = getContentPane(); JButton bytData = new JButton("Byt data"); JButton valdaRader = new JButton("Valda rader"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(400,300); try { MysqlDB.kopplaUpp(); ResultSet rs = MysqlDB.statement.executeQuery("SELECT * FROM Person"); String[] headers = DBMethods.getHeaders(rs); Object[][] content = DBMethods.getContent(rs); tableModel = new DefaultTableModel(content, headers); // Uppg 6 table = new JTable(tableModel); // Uppg 6 table = new JTable(content, headers); // Uppg 3 valdaRader.addActionListener(new VL()); // Uppg 5 bytData.addActionListener(new AL()); // Uppg 6 scrollPane = new JScrollPane(table); c.add(scrollPane, BorderLayout.CENTER); c.add(valdaRader, BorderLayout.NORTH); c.add(bytData, BorderLayout.SOUTH); } catch(SQLException e) { System.out.println(e); } DBMethods.setColumnWidth(table, new int[]{120,120,40,40}); // Relativa bredder setVisible(true); } public void setTableContent(Object[][] content, Object[] headers) { tableModel.setDataVector(content, headers); } // Uppg 5 private class VL implements ActionListener { public void actionPerformed(ActionEvent e) { int[] selectedRows = table.getSelectedRows(); String res=""; for(int i=0; i<selectedRows.length; i++) { res += ""+selectedRows[i]+". "+table.getValueAt(selectedRows[i],0)+"\n"; } System.out.println(res); } } // Uppg 6 private class AL implements ActionListener { public void actionPerformed(ActionEvent e) { try { ResultSet rs = MysqlDB.statement.executeQuery("SELECT * FROM Telefon"); tableModel.setDataVector(DBMethods.getContent(rs), DBMethods.getHeaders(rs)); } catch (SQLException e1) {} } } public static void main(String[] args) { new Uppgift3(); } } DA129A, Programmering 1 6