LINUXSOFT.cz Přeskoč levou lištu

ARCHIV



   

> Java na web IX. - Databáze

V dnešním díle se budeme věnovat databázím. Popíšeme si fungování JDBC, JNDI, driverů, konfiguraci aplikace i aplikačního serveru, instalaci databázového serveru a nakonec si pomocí nabytých znalostí vylepšíme naši aplikaci.

9.7.2013 00:00 | Petr Horáček | Články autora | přečteno 11374×

Databáze jsou téměř neoddělitelnou součástí webových aplikací, naše aplikace z minulého dílu využívala jako databázi jednoduchý Java seznam, to ale není úplně ono. V dnešním díle si představíme práci s SQL databázemi prostřednictvím Tomcatu. Začneme teoretickým úvodem, pokračovat budeme možnostmi konfigurace spojení s databází a nakonec si zakomponujeme databázi i do naší aplikace.

Databáze a Tomcat

Pro komunikaci s databázemi používá Tomcat technologii JDBC (Java Database Connectivity), ta slouží jako prostřední vrstva mezi aplikací a databázovým serverem. Pro práci s konkrétním databázovým systémem využívá JDBC tzv. drivery. Díky tomu se nemusíme starat o nízkoúrovňovou komunikaci s databázovým serverem a pouze využíváme API JDBC.

Aby pod množstvím spojení nedocházelo k přetížení, využívá JDBC takzvaného poolování. Při každém volání databáze není vytvářeno nové spojení, ale využije se spojení vytvořené předchozím dotazem, jde tedy o jakousi recyklaci. Nastavení poolování i připojení k databázi lze snadno provést pomocí JNDI (Java Naming and Directory Interface) Resource.

Instalace databázového serveru

Nyní trochu odbočíme od Javy a popíšeme si instalaci databázového serveru. V naší aplikaci budeme používat databázový systém MySQL (více se o něm můžete dozvědět zde: http://www.linuxsoft.cz/article_list.php?id_kategory=232), pusťme se tedy do jeho instalace:

sudo apt-get install mysql-server

Během instalace budete požádání o vyplnění hesla administrátora databázového serveru. Pokud nechcete aby se server spouštěl při každém spuštění počítače a nezatěžovat zbytečně systém, proveďte následující kroky: Otevřete soubor mysql.conf ve složce /etc/init:

sudo nano /etc/init/mysql.conf

Pomocí dvojité mřížky (#) zakomentujte řádky začínající „start on“ a soubor uložte (Ctrl + X, yes). Nyní je vždy pro spuštění databázového serveru nutné zadat příkaz:

sudo service mysql start

Konfigurace

A nyní se již dejme do konfigurace Tomcatu a aplikace samotné.

JDBC Driver

Nejprve je nutné přidat knihovnu driveru používané databáze, v našem případě tedy MySQL. Pokud se rozhodnete umístit knihovnu přímo do aplikace, stačí v projektu NetBeans kliknout pravým tlačítkem na záložku Libraries, Add Library a vyhledat knihovnu MySQL JDBC Driver.

Pokud chcete knihovnu zpřístupnit hromadně všem aplikacím na aplikačním serveru, stáhněte ji z adresy http://dev.mysql.com/downloads/connector/j/ a její JAR archiv vložte do složky lib v místě instalace Tomcatu.

JNDI Resource, možnost A

Konfigurace přístupu k databázi probíhá pomocí tzv. JNDI Resource, ten lze použít v různém rozsahu a na různém umístění.

První možností je jeho umístění přímo v souboru context.xml aplikace a mít tak všechno nastavení na jednom místě. V tomto případě bude konfigurace načtena při startu aplikace a přístup k databázi bude umožněn pouze jí. Soubor context.xml je umístěn ve složce META-INF aplikace a v základu může vypadat nějak takto:

<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/JNW">
  
</Context>

Všimněte si, že pod atributem path se nachází kořenová adresa aplikace. Pro přidání resource je nutné do těla tagu Context přidat následující:

<Resource auth="Container" driverClassName="com.mysql.jdbc.Driver"
    maxActive="15" maxIdle="3"
    name="jdbc/nazevDatabaze" type="javax.sql.DataSource"
    url="jdbc:mysql://server:3306/nazevDatabaze?useEncoding=true&amp;characterEncoding=UTF-8"
    username="jmeno" password="heslo" validationQuery="/* ping */ SELECT 1"/>

DriverClassName zadává cestu ke třídě driveru, maxActive nastavuje maximální počet aktivních spojení, maxIdle pak nastavuje maximální počet připravených a neaktivních spojení čekajících v poolu na dotazy, name udává název databáze, přes který budeme ke zdroji přistupovat, pomocí url zadáváme adresu k databází (navíc přidáme parametry pro správné kódování), username a password udává přístupové údaje, validationQuery je dotaz prováděný vždy před připojením k databázi (pomocí SELECT 1 vždy „pingneme“ na spojení a zamezíme tak problémům s již ukončeným spojením).

JNDI Resource, možnost B

Druhou možností konfigurace je definování Resource v souboru Tomcatu server.xml, v tomto případě budou moci komunikovat s databázi všechny aplikace. Pokud chcete využívat tento způsob, vložte do souboru server.xml tento kód (pozn.: tag GlobalNamingResources už nejspíš někde v souboru bude):

<GlobalNamingResources>
    <Resource auth="Container" driverClassName="com.mysql.jdbc.Driver"
        maxActive="15" maxIdle="3"
        description="Global Naming Resource"
        name="jdbc/nazevGlobalDatabaze" type="javax.sql.DataSource"
        url="jdbc:mysql://server:3306/nazevDatabaze?useEncoding=true&amp;characterEncoding=UTF-8"
        username="jmeno" password="heslo" validationQuery="/* ping */ SELECT 1"/>
</GlobalNamingResources>

Jak vidíte, jediný rozdíl oproti předchozímu způsobu je umístění a také obalení Resource tagem GlobalNamingResources.

Tip: Úpravu konfiguračního souboru Tomcatu server.xml lze provést přímo z NetBeans. Přesuňte se do karty Services, rozklikněte záložku Servers, klikněte pravým tlačítkem na server Apache Tomcat a zvolte Edit server.xml.

Pokud chcete takto definovaný zdroj využívat v aplikaci musíte vytvořit tzv. ResourceLink, ten je třeba vložit do souboru context.xml aplikace a může vypadat například takto:

<ResourceLink name="jdbc/nazevDatabaze"
global="jdbc/nazevGlobalDatabaze"
type="javax.sql.DataSource" />

Pomocí parametru name budeme přistupovat ke spojení z aplikace, parametr global udává název zdroje zadaný v GlobalNamingResources.

Získání spojení

Posledním krokem před samotným zasíláním dotazů je získání spojení s databází:

Context initCtx = new InitialContext();
Context ctx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) ctx.lookup("jdbc/nazevDatabaze");
Connection conn = ds.getConnection();

Nejprve získáme kontext aplikace, poté se v něm pokusíme pomocí nadefinovaného názvu najít zdroj dat a z něj nakonec získáme samotné spojení.

Rozšíření aplikace

Vraťme se nyní k naší aplikaci na níž si použití databází předvedeme.

Vytvoření databáze a tabulky

Nejprve musíme pro naši aplikaci vytvořit databázi, využijeme pro to prostředí NetBeans. Přesuňte se do karty Services, klikněte pravým tlačítkem na záložku Databases a zvolte New connection. Objeví se před vámi průvodce, v prvním okně vyberete Driver, v dalším vyplníte přihlašovací údaje k databázovému serveru, položku Database vymažte. Další okno nechejte být a v posledním okně zadejte libovolný název spojení. Vytvořené spojení se objeví pod záložkou Databases, klikněte na něj pravým tlačítkem a zvolte Execute Command. Do nového editačního okna vložte tento příkaz a spusťe jej ikonkou „Run SQL“:

CREATE DATABASE JNW;

To vytvoří novou databázi JNW. Klikněte pravým tlačítkem na před chvílí vytvořené spojení v levém panelu a zvolte Referesh, teď byste měli v seznamu databází vidět i tu vaši. Klikněte na ni pravým tlačítkem a zvolte Set as default catalog, poté ji rozklikněte, na položku Tables klikněte pravým tlačítkem, stiskněte Execute Command a vložte příkaz:

CREATE TABLE zapisky ( 
  id int AUTO_INCREMENT,
  nadpis varchar(30),
  obsah varchar(300),
  PRIMARY KEY (id)
);

Nyní by měla být vytvořena nová tabulka zapisky s indexem id a dvěma textovými poli nadpis a obsah. Když kliknete pravým tlačítkem na záložku Tables a stisknete Refresh, měla by pod záložku přibýt nová tabulka, klikněte na ni pravým tlačítkem, View Data. Objeví se data uložená v tabulce (zatím žádná nejsou), je možné je editovat, mazat či přidávat nové, to vše v grafickém rozhraní.

Grafické prostředí NetBeans

Konfigurace Resource

Konfiguraci připojení k databázi umístíme do souboru context.xml aplikace, ještě před tím ale klikněte na záložku Libraries a importujte knihovnu MySQL JDBC Driver.

Nyní můžete v kartě Configuration files otevřít soubor context.xml, pokud jste jej ještě neupravovali, bude se zde nejspíš nacházet jediný uzavřený tag Context. Udělejte z něj párový tag a dovnitř vložte konfiguraci přístupu do databáze. Výsledný kód může vypadat nějak takto:

<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="[původníUrlAplikace]">
    <Resource auth="Container" driverClassName="com.mysql.jdbc.Driver"
        maxActive="15" maxIdle="3"
        name="jdbc/mysql" type="javax.sql.DataSource"
        url="jdbc:mysql://localhost:3306/JNW?useEncoding=true&amp;characterEncoding=UTF-8"
        username="[jmeno]" password="[heslo]" validationQuery="/* ping */ SELECT 1"/>
</Context>

Úprava modelu

Musíme také trochu upravit model Zapisek. S přechodem na databázi nám totiž přibyla nová proměnná id, kterou jsme dosud získávali ze seznamu. Upravená třída bude vypadat takto:

package modely;

public class Zapisek {
    
    private int id;
    private String nadpis;
    private String obsah; 
    
    public Zapisek(String nadpis, String obsah){
        this.nadpis = nadpis;
        this.obsah = obsah;
    }
    
    public Zapisek(int id, String nadpis, String obsah){
        this.id = id;
        this.nadpis = nadpis;
        this.obsah = obsah;
    }
    
    public void setId(int id){
        this.id = id;
    }
    
    public void setNadpis(String nadpis){
        this.nadpis = nadpis;
    }
    
    public void setObsah(String obsah){
        this.obsah = obsah;
    }
    
    public int getId(){
        return id;
    }
    
    public String getNadpis(){
        return nadpis;
    }
    
    public String getObsah(){
        return obsah;
    }  
}

Práce s databází

Pro práci s databází si vytvoříme v záložce Source Packages nový balíček databaze, do něj vložte novou Java Class s názvem UpravaZapisku. V této třídě se budou nacházet všechny operace probíhající mezi zápisky a databází, tedy: získání všech zápisků, získání konkrétního zápisku, uložení zápisku, přidání zápisku a smazání zápisku.

Abychom se nemuseli opakovat ve vypisování kódu pro získání spojení, vytvoříme si jednoduchou metodu:

private Connection getConnection() throws NamingException, SQLException {
    Context initCtx = new InitialContext();
    Context ctx = (Context) initCtx.lookup("java:comp/env");
    DataSource ds = (DataSource) ctx.lookup("jdbc/mysql");
    return(ds.getConnection());
}

getZapisky()

Další metoda bude sloužit k získání všech uložených zápisků, zde je její kód:

public List<Zapisek> getZapisky() throws SQLException {
    Connection connection = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;       
    List<Zapisek> zapisky = new ArrayList();

    try {
        String query = "SELECT * FROM zapisky";            
        connection = getConnection();            
        stmt = connection.prepareStatement(query);           
        rs = stmt.executeQuery();
            
        while (rs.next()) {
            zapisky.add(new Zapisek(rs.getInt("id"), rs.getString("nadpis"), rs.getString("obsah")));
        }
    } catch (NamingException ex) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SQLException sQLException) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, sQLException);
    } finally {
        if(rs != null) {rs.close();}
        if(stmt != null) {stmt.close();}
        if(connection != null) {connection.close();}
    }
    return zapisky;
}

Jak vidíte, metoda getZapisky() vrací seznam modelů Zapisek. Nejdříve si přichystáme potřebné třídy Connection, PreparedStatement a ResultSet z balíčku java.sql.* a prázdný seznam zapisky.

Další část kódu je uzavřená ve vyjímce try, catch, finally, ve které zachycujeme chyby NamingException a SQLException. Do proměnné query vložíme samotný SQL dotaz, do proměnné connection přiřadíme spojení získané vytvořenou metodou. Do PreparedStatementu stmt přichystáme dotaz a nakonec dotaz odešleme a odpověď databáze zapíšeme do ResultSetu rs.

Pomocí cyklu while s podmínkou rs.next() projdeme všechny vrácené řádky. Ke sloupcům ve vrácených řádcích můžeme přistupovat pomocí jejich názvu (např.: rs.getInt('id'); rs.getString('nadpis'); a podobně) nebo pořadí (např.: rs.getInt(1);). V našem případě získaná data zapíšeme do seznamu.

Zvlášť důležité je nevynechat závěrečný kód finally, v něm ukončujeme spojení a vracíme jej do poolu. Pokud bychom tak neučinili, žádná spojení by se nevracela a zanedlouho by došlo k jejich vyčerpání (dosažení hodnoty maxActive).

getZapisek()

Velmi podobná bude i metoda pro získání zápisku definovaného pomocí jeho ID:

public Zapisek getZapisek(int id) throws SQLException {
    Connection connection = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;        
    Zapisek zapisek = null;

    try {
        String query = "SELECT * FROM zapisky WHERE id = ?";            
        connection = getConnection();            
        stmt = connection.prepareStatement(query); 
        stmt.setInt(1, id);
        rs = stmt.executeQuery();
            
        while (rs.next()) {
            zapisek = new Zapisek(rs.getInt("id"), rs.getString("nadpis"), rs.getString("obsah"));
        }
    } catch (NamingException ex) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SQLException sQLException) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, sQLException);
    } finally {
        if(rs != null) {rs.close();}
        if(stmt != null){stmt.close();}
        if(connection != null){connection.close();}
    }
    return zapisek;
}

Kód je velice podobný, všimněte si rozdílu zde:

String query = "SELECT * FROM zapisky WHERE id = ?";            
connection = getConnection();            
stmt = connection.prepareStatement(query); 
stmt.setInt(1, id);
rs = stmt.executeQuery();

V dotazu query se objevil otazník, ten udává místo pro vložení proměnné, ta je definována na čtvrtém řádku, kde za první otazník dosazujeme proměnnou id. Pomocí třídy PreparedStatement provedeme korektní dosazení hodnot do dotazu a zároveň ochráníme databázi před SQL injection útoky.

setZapisek()

Další velice podobný kód, tentokrát používáme příkaz UPDATE, a proto místo metody stmt.executeQuery() využijeme stmt.executeUpdate(). Jako výsledek této operace můžeme přijmout například index uloženého zápisku, ten ale nyní nepotřebujeme, a proto necháme odpověď netknutou.

public void setZapisek(int id, String nadpis, String obsah) throws SQLException {
    Connection connection = null;
    PreparedStatement stmt = null;
        
    try {
        String query = "UPDATE zapisky SET nadpis = ?, obsah = ? WHERE id = ?";            
        connection = getConnection();            
        stmt = connection.prepareStatement(query); 
        stmt.setString(1, nadpis);
        stmt.setString(2, obsah);
        stmt.setInt(3, id);
        stmt.executeUpdate();            
    } catch (NamingException ex) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SQLException sQLException) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, sQLException);
    } finally {
        if(stmt != null){stmt.close();}
        if(connection != null){connection.close();}
    }
}

addZapisek()

U příkazu INSERT opět využíváme stmt.executeUpdate().

public void addZapisek(String nadpis, String obsah) throws SQLException {
    Connection connection = null;
    PreparedStatement stmt = null;
        
    try {
        String query = "INSERT INTO zapisky (nadpis, obsah) VALUES (?, ?)";            
        connection = getConnection();            
        stmt = connection.prepareStatement(query); 
        stmt.setString(1, nadpis);
        stmt.setString(2, obsah);
        stmt.executeUpdate();          
    } catch (NamingException ex) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SQLException sQLException) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, sQLException);
    } finally {
        if(stmt != null){stmt.close();}
        if(connection != null){connection.close();}
    }
}

removeZapisek()

A poslední metoda využívající příkaz DELETE.

public void removeZapisek(int id) throws SQLException {
    Connection connection = null;
    PreparedStatement stmt = null;
        
    try {
        String query = "DELETE FROM zapisky WHERE id = ?";            
        connection = getConnection();            
        stmt = connection.prepareStatement(query); 
        stmt.setInt(1, id);
        stmt.executeUpdate();            
    } catch (NamingException ex) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SQLException sQLException) {
        Logger.getLogger(UpravaZapisku.class.getName()).log(Level.SEVERE, null, sQLException);
    } finally {
        if(stmt != null){stmt.close();}
        if(connection != null){connection.close();}
    }
}

Controller

Nyní provedeme úpravu controlleru, ten bude nyní využívat metody a funkce třídy UpravaZapisku.

public class Controller extends HttpServlet {   
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {

        String adresa = request.getServletPath();
        UpravaZapisku upravaZapisku = new UpravaZapisku();
        
        if(adresa.equals("/zapisky")) {
            try {
                List<Zapisek> zapisky = upravaZapisku.getZapisky();
                request.setAttribute("zapisky", zapisky);
                request.getRequestDispatcher("/WEB-INF/view/zapisky.jsp").forward(request, response);    
            } catch (SQLException ex) {
                Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
            }            
        }
        else if(adresa.equals("/upravit")){
            try {
                int id = Integer.parseInt(request.getParameter("id"));
                Zapisek zapisek = upravaZapisku.getZapisek(id);
                request.setAttribute("zapisek", zapisek);
                request.getRequestDispatcher("/WEB-INF/view/upravit.jsp").forward(request, response);
            } catch (SQLException ex) {
                Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }        
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        String adresa = request.getServletPath();
        request.setCharacterEncoding("UTF-8");
        UpravaZapisku upravaZapisku = new UpravaZapisku();
        
        if(adresa.equals("/pridat")) {
            String nadpis = request.getParameter("nadpis");
            String obsah = request.getParameter("obsah");            
            if(!nadpis.isEmpty() && !obsah.isEmpty()){
                try {
                    upravaZapisku.addZapisek(nadpis, obsah);
                } catch (SQLException ex) {
                    Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                }
                presmeruj(request, response, "");
            }
            else {
                presmeruj(request, response, "?upozorneni=True");
            }           
        }
        else if(adresa.equals("/ulozitupravy")){
            int id = Integer.parseInt(request.getParameter("id"));
            String nadpis = request.getParameter("nadpis");
            String obsah = request.getParameter("obsah");            
            if(!nadpis.isEmpty() && !obsah.isEmpty()){
                try {
                    upravaZapisku.setZapisek(id, nadpis, obsah);
                } catch (SQLException ex) {
                    Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                }
                presmeruj(request, response, "");
            }
            else {
                presmeruj(request, response, "upravit?id=" + id + "&upozorneni=True");
            }
        }
        else if(adresa.equals("/smazat")){
            int id = Integer.parseInt(request.getParameter("id"));
            try {
                upravaZapisku.removeZapisek(id);
            } catch (SQLException ex) {
                Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
            }
            presmeruj(request, response, "");
        }
        else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }       
    }

Viewy

Na závěr si upravíme viewy aplikace, zde budou změny nejmenší.

V souboru zapisky.jsp, změníme všechny „indexy“ na „id“. ID zápisků už nebudeme získávat z varStatus-u ale ze samotného zápisku:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib tagdir="/WEB-INF/tags" prefix="m" %>

<m:Base titulek="Zápisky">
    <h1>Zápisník</h1>
    
    <form action="<c:url value='/pridat' />" method="POST">
        <label for="nadpis">Nadpis</label>
        <input type="text" name="nadpis" />
        <br>
        <textarea name="obsah" cols="40" rows="5"></textarea>
        <br>
        <input value="Přidat" type="submit" />
    </form>

    <c:if test="${param.upozorneni}">
        <span>Musíte vyplnit obě pole.</span>
    </c:if>

    <c:choose>
        <c:when test="${not empty zapisky}">
            <c:forEach var="zapisek" items="${zapisky}">
                <div class="zapisek">

                    <div class="nadpis"><c:out value="${zapisek.nadpis}"/></div>

                    <div class="tlacitka">
                        <form method="GET" action="<c:url value='/upravit'/>">
                            <input type="hidden" value="${zapisek.id}" name="id" />
                            <input type="submit" value="Upravit"/>
                        </form>                               
                        <form method="POST" action="<c:url value='/smazat'/>">
                            <input type="hidden" value="${zapisek.id}" name="id" />
                            <input type="submit" value="Smazat"/>
                        </form>                               
                    </div>

                    <div class="obsah"><c:out value="${zapisek.obsah}"/></div>

                </div>
            </c:forEach>
        </c:when>
        <c:otherwise>
            <span>Dosud nebyl přidán žádný zápisek.</span>
        </c:otherwise>
    </c:choose>
           
</m:Base>

V souboru upravit.jsp už nebudeme získávat id zápisku z URL, ale také ze samotného zápisku:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib tagdir="/WEB-INF/tags" prefix="m" %>

<m:Base titulek="Zápisky">
    <h1>Zápisník - úprava</h1>
    
    <form action="<c:url value='/ulozitupravy' />" method="POST">
        <input type="hidden" name="id" value="${zapisek.id}" />
        <label for="nadpis">Nadpis</label>
        <input type="text" name="nadpis" value="<c:out value='${zapisek.nadpis}' />" />
        <br>
        <textarea name="obsah" cols="40" rows="5"><c:out value='${zapisek.obsah}' /></textarea>
        <br>
        <input value="Upravit" type="submit" />
    </form>

    <c:if test="${param.upozorneni}">
        <span>Musíte vyplnit obě pole.</span>
    </c:if>
</m:Base>

Závěr

To je k dnešnímu dílu vše, nyní můžete spustit aplikaci a vyzkoušet její staronové funkce. V příštím díle se budeme věnovat autentizaci a autorizaci.

Zdrojové kódy aplikace naleznete na GitHubu: https://github.com/PetrHoracek/JavaNaWeb

Verze pro tisk

pridej.cz

 

DISKUZE

Nejsou žádné diskuzní příspěvky u dané položky.



Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

28.11.2018 23:56 /František Kučera
Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.
Komentářů: 1

12.11.2018 21:28 /Redakce Linuxsoft.cz
22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář

6.11.2018 2:04 /František Kučera
Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

4.10.2018 21:30 /Ondřej Čečák
LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář

18.9.2018 23:30 /František Kučera
Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

9.9.2018 14:15 /Redakce Linuxsoft.cz
20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business. Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář

12.8.2018 16:58 /František Kučera
Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář

16.7.2018 1:05 /František Kučera
Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

31.7.2023 14:13 / Linda Graham
iPhone Services

30.11.2022 9:32 / Kyle McDermott
Hosting download unavailable

13.12.2018 10:57 / Jan Mareš
Re: zavináč

2.12.2018 23:56 / František Kučera
Sraz

5.10.2018 17:12 / Jakub Kuljovsky
Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze