* Свързване на Java с MySQL
Публикувано на 07 март 2013 в раздел Бази от Данни.
В тази статия ще бъде показан най-елементарен пример за свързване на програма на Java с база от данни на MySQL. За целта освен инсталирани MySQL сървър и наличие на JDK ще ни е нужен драйвера Connector/J. Него трябва да го изтеглите от сайта на MySQL.
Нека първо да създадем една елементарна база от данни и да създадем потребител за нея:
CREATE DATABASE mydb; USE mydb; CREATE TABLE students( fn BIGINT(12) UNSIGNED PRIMARY KEY, name VARCHAR(255) NOT NULL, grp TINYINT(2) UNSIGNED NOT NULL )ENGINE=InnoDB; GRANT ALL ON mydb.* TO 'philip'@'localhost' IDENTIFIED BY 'topsecretpass'; INSERT INTO students(fn, name, grp) VALUES (1231234111, "Petar Petrov", 65), (1231234115, "Ivan Ivanov", 67), (1231234095, "Maria Petrova", 65);
Сега искаме да направим приложение на Java, което се свързва чрез потребител "philip" с тази база от данни и отпечатва съдържанието на таблица students в конзолата. Стъпките за това са:
1. Създайте нов проект (project) в средата, която използвате, за да програмирате на Java (Eclipse, Netbeans, DrJava и т.н.);
2. В директорията на проекта създайте поддиректория "lib";
3. От архива на Connector/J, който по-рано изтеглихте, разархивирайте в директория lib файла mysql-connector-java-5.1.23-bin.jar (версията може да се различава, в зависимост коя е последната налична);
4. Във вашия проект прибавете този jar файл като библиотека. В Eclipse това става чрез "Properties > Java Build Path > Libraries". В NetBeans натиснете с десен бутон на проекта и изберете Properties, изберете Libraries -> Add JAR/Folder. В DrJava отидете на Projects > Project Properties и добавете файла в "Extra Classpath".
Вече сте готови да пишете код на Java, с който ще изпълнявате заявки и ще получавате отговори от MySQL сървър. Ето нашия примерен код (с коментари):
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; public class myprogram{ // метод който се свързва със SQL сървъра и връща ресурс (връзка) към него, // а ако свързването е неуспешно, ще върне NULL private static Connection connect(String url, String user, String password){ Connection result = null; try{ result = DriverManager.getConnection(url, user, password); } catch(SQLException e){ e.printStackTrace(); } return result; } public static void main(String[] args){ // localhost е IP на сървъра, 3306 е порта, mydb е базата String url = "jdbc:mysql://localhost:3306/mydb"; String user = "philip"; String pass = "topsecretpass"; Connection link = myprogram.connect(url, user, pass); if(link == null){ System.out.println("Опа, MySQL не е пуснат!"); return; } else{ System.out.println("Свързах се с MySQL сървъра!"); } // В JDBC Statement е заявката, а ResultSet е резултата от нея Statement stmt = null; ResultSet resultSet = null; try{ stmt = link.createStatement(); resultSet = stmt.executeQuery("SELECT fn, name, grp FROM students"); // Обхождаме резултатната таблица ред по ред и отпечатваме на екрана while (resultSet.next()) { System.out.print("FN: "+resultSet.getLong("fn")); System.out.print(", Name: "+resultSet.getString("name")); System.out.println(", Group: "+resultSet.getShort("grp")); } } catch(SQLException e){ e.printStackTrace(); } finally{ try{ if(stmt != null) stmt.close(); if(resultSet != null) resultSet.close(); if(link != null) link.close(); } catch(SQLException e2){ e2.printStackTrace(); } } } }
Виждате, че заявка SELECT се изпълни с метод "excecuteQuery", което връща като резултат ResultSet. При INSERT, UPDATE и DELETE се използва "executeUpdate()" - като резултат този метод връща броя на редовете, които са били вмъкнати/обновени/изтрити.
Ако желаете да изпълнявате параметризирани заявки, трябва да използвате клас java.sql.PreparedStatement. Ето как можем да направим INSERT параметризирана заявка:
PreparedStatement preparedStatement; preparedStatement = link.prepareStatement( "INSERT INTO students(fn, name, grp) VALUES (?, ?, ?)" ); preparedStatement.setLong(1, 1231234100L); preparedStatement.setString(2, "Dimitar Dimitrov"); preparedStatement.setShort(3, (short)66); int rowsInserted = preparedStatement.executeUpdate();
Ето как се извършва параметризирана SELECT заявка:
PreparedStatement preparedStatement; preparedStatement = link.prepareStatement( "SELECT fn FROM students WHERE name = ?" ); preparedStatement.setString(1, "Petar Petrov"); ResultSet resultSet = preparedStatement.executeQuery();
Добре ,а когато искаме да въвеждаме самата заявка от клавиатурата и на екрана да ни се извежда резултата как постъпваме?
Четеш от някакъв вход (примерно със Scanner от System.in или пък от текстово поле в Swing), записваш резултата в променлива от тип String и подаваш тази променлива като параметър.
Ами ако искаме да използваме агрегатна функция в заявката в eclipse ?
Например:
resultSet = stmt.executeQuery("select avg(amount) from ...");
output.println(resultSet.getString("avg(amount)");
получавам грешка, гласяща, че колоната avg(amount) не съществува, а заявката си е съвсем легитимна, тъй като работи в конзолата на mysql.
съжалявам, май е моя грешката, изтрийте горното..
1. ResultSeta-a винаги връща таблица като резултат, нали? И за да се изкара на конзолата трябва да се обходи с while цикъл? Дори кога връщания резултат е само един ( факултетния номер на Petar Petrov).
Забравих 2рия въпрос:
2. Има ли някакви специални exception-и за липса на търсената колона примерно, липса на такъв ред в таблицата и т.н? Или трябва ние да си ги създаваме?
Първи въпрос: да, все едно имаш двумерен масив, който обхождаш ред по ред. Ако си убеден, че е само един ред (например в where условието има сравнение по unique колона), естествено няма нужда от цикъл.
Втори въпрос: има ис SQLException - в примера е показано.
Искам да попитам нещо.Ситуацията е следната: извеждаме със SELECT заявка данни от дадена таблица, при коетпо има някои полета със стойност NULL .И въпросът ми е как да правим проверка за всяка прочетена стойност от дадено поле дали е NULL ,ако е да не показва нищо във съответното текстово поле и ако е различно от NULL да се покаже в полето стойността .Благодаря предварително!
В Java можеш да проверяваш така:
if(var == null)...
Когато създадем едно десктоп приложение използващо mysql сървър за достъп до базата си данни, какво трябва да направим когато искаме това приложение да се използва и от други компютри като изключим варианта да се свързват със сървъра чрез интернет. Можем ли да инсталираме някак си само една самостоятелна база данни във всеки от компютрите. Без цялата система за бази данни. Само една - нужната за приложението база данни.
Може, но не с MySQL. Наричат се "embedded" или "serverless" системи за управление на бази от данни. Може би най-популярна от тях е SQLLite. На Microsoft мисля, че се казваше SQL Server Compact или нещо от сорта.
Ако имам член-променлива Statement stmt, как е по-добре - да я инициализирам в конструктора и след това в отделните методи само да викам stmt.executeQuery, или във всеки метод да имам
stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("..."); ?
В повечето случаи се прави следното - връзката (link) се отваря в метода, извършва се действието и се затваря възможно най-бързо.
Можеш да поддържаш и отворена връзка през цялото време, но това обикновено води до проблеми при многопотребителски системи.
На мен ми излиза грешка като се опитам да се свържа със сървъра: Packet for query is too large (7696217 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
В нета пише : That error is coming from neither the MySQL server nor from
DBVisualizer. That is coming from your JDBC driver. Check the
version of that and research the effect of configuration options.
Версията на mysql ми е 5.6.16. Какво да правя?
Случайно да изпращаш големи файлове към базата от данни - например аудио файл или нещо друго голямо?
Намери файла my.ini и в него добави:
max_allowed_packet=32M
След това рестартирай сървъра и би трябвало да работи.
Ще можете ли да ми кажете къде имам грешка?:
statement = link.createStatement();
resultSet = statement.executeQuery("select book.book_name, category.category_name, author.firstname, author.lastname, book.pub_date " +
"from book join author on book.author_id = author.id " +
"join category on book.category_id = category.id" +
"group by book.book_name");
while (resultSet.next()){
System.out.println("Book name: " + resultSet.getString("book.book_name") + ",\t\t"
+ "Category: " + resultSet.getString("category.category_name") + ",\t\t"
+ "Author name: " + resultSet.getString("author.firstname")
+ " " + resultSet.getString("author.lastname") + ",\t\t"
+ "Published date: " + resultSet.getString("book.pub_date"));
}
Изкарва ми следното:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'by book.book_name' at line 1
Сложи поне един интервал между "category.id" и "group by"
Благодаря Ви много :)
Искам да ви попитам как мога да изпратя resultset-а чрез socket на клиента. В повечето сайтове пише че било невъзможно. Ако да какъв избор имам ?
Resultset не е Serializable, съответно не може да се трансферира през сокет. Трябва да го превърнеш в нещо, което е Serializable и чак тогава да използваш Object(Input/Output)Stream. Например някакъв RowSet - например CachedRowSet.
По този начин ще се получи ли?
String message;
message=resultSet.getString("name");
out.writeUTF(message);
Изпращането на String не трябва да е никакъв проблем.
при извъшване на повече от една заявка за въвеждане от servera към базата данни ми изписва този error java.io.StreamCorruptedException: invalid stream header: 7372000D
може ли да ми кажете от какво е ?
Или не си прочел първия път до края, или нещо друго си объркал. Очакваш да ти се подаде начало на Stream, а ти изпращат друго (вероятно някаква буква от поредицата на предишния поток).