Wykonywanie kodu sql (creat schema / create table) w kodzie java

0

Cześć!
Zawitałem na Wasze forum jako pytający ponieważ zadałem już kilka podobnych pytań na stackoverflow i nikt nie potrafi mi tam pomóc.
Problem jest dość skomplikowany. Mam do napisania aplikację webową (spring | mysql | thymyleaf) która będzie działała trochę jak MySQL Workbench online.
Chodzi o to że każdy użytkownik strony będzie mógł utworzyć na niej bazę danych, jedną, drugą, trzecią, ile będzie chciał. A następnie będzie mógł wykonywać podstawowe operacje na tych bazach. Czyli CRUD po prostu.

Wymyśliłem sobie to w ten sposób:

  1. Kiedy użytkownik wciśnie dajmy na to przycisk, "utwórz bazę danych", to zostanie utworzony plik.sql z unikalną nazwą, do tego pliku.sql zostanie zapisany odpowiedni kod sql który jest odpowiedzialny za utworzenie tej bazy. Czyli np. ktoś klika utwórz bazę danych, wpisuje nazwe "mojabaza", wtedy ja tworzę plik mojabaza.sql, otwieram go i zapisuję do niego:
    Create schema mojabaza;
  2. Kolejny przykład, użytkownik chce utworzyć tabelę o nazwie "mojatabela" z polami x,y,z. Wtedy ja z powrotem otwieram ten jego plik mojabaza.sql i wpisuję tam kod sql odpowiedzialny za utworzenie tej tabeli.
  3. Koniec tej historii jest taki że mam plik.sql w którym jest cała baza użytkownika gotowa do tego aby wkleić ją do jakiegoś interpretera sql i wykonać. W mojej bazie głównej będę trzymał tylko i wyłącznie ścieżki do tych plików.

**No i tutaj zaczynają się schody... ** Ponieważ tak jak wykonanie po stronie kodu CRUD'a jest bezproblemowe, to jak mam wykonać taki kod jak "Create schema / czy Create table?
Podszedłem do tego w ten sposób że skoro w moim pliku konfiguracyjnym application.properties jestem połączony z moją bazą no to nie jest to dziwne że nie moge wykonać polecenia "create schema". Więc zmieniłem mój plik konfiguracjny.

Poprzednio tak wyglądało moje połączenie z bazą:
spring.datasource.url=jdbc:mysql://localhost:3306/mojabaza
spring.datasource.username=root
spring.datasource.password=student

a teraz wygląda tak:
spring.datasource.url=jdbc:mysql://localhost:3306/
spring.datasource.username=root
spring.datasource.password=student

Usunąłem nazwę bazy i teraz na moją logikę jestem połączony po prostu z samym serwerem SQL. Sprawdzałem na testowym projekcie i kiedy tak zrobię to wszystkie polecenia SQL działają, create schema również.
Niestety problemy zaczynają się kiedy zrobię coś takiego w moim głównym projekcie -.= Ponieważ tam używam Encji. Wydawało mi się że jedyne co będę musiał zmienić no to zapytania.
Zamiast pisać takie selecty:
SELECT * FROM tabela

To wystarczy przerobić to na coś takiego:
SELECT * FROM mojabaza.tabela

W moich encjach dodałem w adnotacji @Table parametr schema = "mojabaza"

@Entity
@Table(name = "role", schema = "mojabaza")
public class Role {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "role_id")
	private int id;

	@Column(name = "role_name")
	private String role;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}

}

Tak czy inaczej mam masę błędów podczas próby wykonania tych encji przez kompilator. "No database selected", takiego typu błędy dostaję. Myślałem że będę mógł korzystać po prostu ze wszystkich baz które mam w workbenchu wystarczy że usunę nazwę bazy w połączeniu z bazą no i sprecyzuję wszędzie w kodzie z której bazy należy korzystać.

**
Sam już nie wiem czy to dobry sposób na rozwiązanie tego problemu... Raczej nie wiem nawet czy to jest wykonalne w ten sposób. Czy ktoś miałby dla mnie jakieś porady jak sobie z tym poradzić? Może powinienem inaczej podejść do tematu? Nie mam innego pomysłu jak wykonać cały ten kod sql... Jest to mój pierwszy projekt w springu.
**

To są linki do pytań które zadawałem na stackoverflow na ten sam temat:
https://stackoverflow.com/questions/59347501/how-to-create-mysql-database-from-java-code
https://stackoverflow.com/questions/59358493/how-to-use-multiple-databases-with-spring

1

W application.properties powinna być prawidłowa ścieżka na db, w przeciwnym razie spring może mieć problem z identyfikacją resourców, i wydaje mi się, że usunięcie nazwy mojabaza było zbędne.

Schema można zrobić wykorzystując JDBC, ale nie wiem jak z uprawnieniami, bo nie miałem okazji robić takich projektów. No mam zastrzeżenie, żeby nowy user miał możliwość tworzenia nowej db... to raczej słaby pomyśł, dałbym jakieś ograniczenie na ilość userów i db na usera. Inna sprawa to taka, że nie wiem czy Mysql to najlepszy wybór do tego.

1

Dzięki temu TWOJA_DB?createDatabaseIfNotExist=true możesz utworzyć bazę za pomoca samego jdbc, sprawdzone w bojach - czasami rzeczywiście się przydaje

A tu pełny zapis
spring.datasource.url= jdbc:mysql://localhost:3306/TWOJA_DB?createDatabaseIfNotExist=true&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

0

Dzięki za dotychczasowe rady, być może się przydadzą.

Niestety Panowie utworzenie 1 bazy mnie nie ratuje. Ja muszę mieć 1 główną moją baze w której trzymam tabele potrzebne do logowania rejestracji itp. a dla userów mojej strony tych baz muszę mieć x. Co to znaczy x?
Użytkownik na mojej stronie musi mieć po prostu panel tak jak jest panel w workbenchu gdzie ma wszystkie swoje bazy.
Jeśli taki użytkownik zaloguje się na stronę i wybierze np. jakąś bazę aby wykonać na niej np. selecta/inserta to ja go muszę na tą bazę przełączyć, czyli wykonać cały ten kod.sql który mam, zapisać zmiany które wprowadzi a następnie jak sie np. wyloguje z konta to całą baze zdropować. Czyli ona w moim workbenchu będzie utworzona tylko przez ten czas przez który on będzie ją "edytował", tak sobie to wyobrażałem.

Wprowadzenie ogarniczenia na liczbę userów itp. mnie raczej nie uratuje bo to (moim zdaniem) nie miałoby sensu. Nie wiem jak miałbym to zrobić. Ograniczenie np. na 100 możliwych kont do rejestracji a potem blokada? Choćby nawet to znaczyłoby że musiałbym przygotować od razu z góry 100x równoległych baz bo podczas rejestracji nowego użytkownika musiałbym brać pod uwage że on utworzy kolejną baze, więc problem nie tkwi tutaj w liczbie userów itp. tylko właśnie w sposobie jak te bazy sensownie tworzyć. No nie wiem, tak mi się przynajmniej wydaje.

Zasłyszałem jeszcze o czymś takim jak tworzenie "podbaz" w jednej głównej bazie danych, wiem że w PostgreSQL da się coś takiego zrobić. Czyli tak jakby bazy danych w bazie danych. Nie wiem tylko czy coś takiego istnieje w mysql, szukałem i jakoś nie widzę żadnych tematów. No ale tu kolega ma rację sam nie jestem pewien czy ten mysql to dobra droga :/ Wybrałem go bo znam go po prostu bardzo dobrze w porównaniu do innych.

1

Ja bym przede wszystkim poszedł w osobne bazy:

  1. do zarządzania aplikacją
  2. do wykonywania różnych SQLi użytkowników

Chodzi o to, żeby zabawy użytkowników nie prowadziły do eskalowania uprawnień i grzebania w danych aplikacji. Mogą to być zupełnie różne silniki bazodanowe.

W ramach bazy 2) wykorzystałbym mechanizmy bazodanowe, które pozwalają odseparować obiekty różnych użytkowników.
W Postgres są ta "schematy", w MySQLu nie mam pojęcia i nie chce mi się szukać ;)

Można wyodrębnić kilka przypadków użycia:

Użytkownik Ziutek rejestruje się w systemie:

  • robię wpisy w bazie#1: konto użytkownika, hasło, etc.

Użytkownik Ziutek tworzy nowy schemat o nazwie "Pierwszy schemat":

  • w bazie#2 zakładam schemat USER_<random_id>
  • w bazie#2 nadaję uprawnienia do schematu USER_<random_id> użytkownikowi aplikacyjnemu (nie Ziutkowi, a temu na którego łączy się aplikacja)
  • w bazie#1 robię wpisy:
    • Ziutek jest właścicielem schematu "Pierwszy schemat"
    • "Pierwszy schemat" jest implementowany przez konkretny schemat na bazie#2 ( Ziutek, "Pierwszy schemat", USER_<random_id> )

Użytkownik Ziutek usuwa schemat:

  • w bazie#2 usuwam schemat USER_<random_id>
  • w bazie#1 usuwam metadane (powiązanie Ziutek, "Pierwszy schemat", USER_<random_id>)

Użytkownik Ziutek wykonuje SQLa na schemacie "Pierwszy schemat":

  • aplikacja bierze SQLa
  • wyciąga dane o fizycznym schemacie, którego należy użyć (czyli z powiązania Ziutka z logicznym schematem "Pierwszy schemat")
  • na bazie#2 aplikacja zmienia aktywny schemat: "SET SEARCH_PATH TO USER_<random_id>;"
  • na bazie#2 aplikacja wykonuje SQLa
  • na bazie#2 aplikacja zmienia aktywny schemat na domyślny

Tworzenie/usuwanie nowego schematu i nadawanie uprawnień wrzuciłbym do procedury i nadał uprawnienia użytkownikowi aplikacyjnemu do wołania tej procedury.

<random_id> - chodzi o to, żeby ograniczyć użytkownikom możliwość zgadywania nazw schematów i grzebania po cudzych schematach wykorzystując odwołanie po aliasach tychże schematów: DELETE FROM FOO.TABELKA;

W przypadku baz fajnie zadbać o ich odpowiednią konfigurację, tak by użytkownicy nie wyciągali danych ze słownika systemowego :-)

0

Znajomy mi podesłał prywatny przykład jak wyglądają takie bazy w Postgre:
1
2

Jedna baza główna na którą zrobione jest połączenie i pod spodem "podbazy" czyli tzw. schematy.
Czy o takie coś Ci chodziło Yarel? Jeśli tak to mam 1 pytanie, bo nigdy nie używałem Postgre. Czym się różni schemat od bazy w Postgre? Bo w mysql są to synonimy schema/database.
Czy te schematy które będę tworzył w mojej bazie głównej mają wszystkie najważniejsze cechy które posiada zwykła baza danych w mysql? Jeśli chodzi o operacje na utworzonych przez Ziutka bazach to potrzebuję tylko żeby sam CRUD się wykonywał. I czy wgl poradzę sobie z Postgre, kiedy znam tylko mysql / oracle? Czy to jest nauka wymagająca sporo czasu? Bo niestety czasu nie mam wiele :<

1
Harord napisał(a):

...

Jedna baza główna na którą zrobione jest połączenie i pod spodem "podbazy" czyli tzw. schematy.
Czy o takie coś Ci chodziło Yarel?
Tak, miałem na myśli schematy tak jak na załączonych screenshotach.

Jeśli tak to mam 1 pytanie, bo nigdy nie używałem Postgre. Czym się różni schemat od bazy w Postgre? Bo w mysql są to synonimy schema/database.

Nie wiem czy to będzie dobra analogia, ale o bazie możesz myśleć jak o dysku twardym, o schematach jak o folderach, a o tabelkach jak o plikach.
Nic nie stoi na przeszkodzie, żeby wszystko trzymać na jednym dysku bez organizowania tego w jakieś logiczne podgrupy (foldery -> schematy), ale łatwiej się zarządza pewnymi rzeczami mając odpowiednią strukturę.

Na poziomie postgresowej bazy danych możesz:

  • zdefiniować w jakim encodingu zapisywane są znaki (UTF-8, ISO-... etc.)
  • zdefiniować, które znaki jak interpretować w kontekście klasy znaków (lower/upper/numerical etc.)
  • zdefiniować collation (porządek sortowanie)
  • ograniczać ilość połączeń
  • zdefiniować dedykowaną przestrzeń tabel (abstrakcja do fizycznej lokalizacji plików bazodanowych)

Czy te schematy które będę tworzył w mojej bazie głównej mają wszystkie najważniejsze cechy które posiada zwykła baza danych w mysql? Jeśli chodzi o operacje na utworzonych przez Ziutka bazach to potrzebuję tylko żeby sam CRUD się wykonywał.

CRUD będą ok. Schematy przechowują "nazwane obiekty", więc dwóch użytkowników różnych schematów będzie mogło stworzyć tabelę o tej samej nazwie.
Z tego co kojarzę, to MySQL ma 2 silniki storagowe: MyISAM i InnoDB i tu mogę być jakieś różnice w funkcjonalności.

I czy wgl poradzę sobie z Postgre, kiedy znam tylko mysql / oracle? Czy to jest nauka wymagająca sporo czasu? Bo niestety czasu nie mam wiele :<

Tego nie wiem, ludzie mają różne zdolności poznawcze. Jeśli znasz MySQL/Oracle, to bliżej będzie z Oracle do Postgresa.

0

Dzięki wielkie za całe info, bardzo mi pomogłeś. Mam jeszcze tylko ostatnie pytanie.
Wspomniałeś że poszedłbyś w 2x bazy nawet na zupełnie różnych silnikach bazodanowych.
Ja mam już całe logowanie i rejestracje z pomocą spring security zrobione na bazie mysql. Czyli teoretycznie mógłbym to już zostawić tylko powinienem teraz dołączyć drugą baze z tego Postgresa, która zajmowałaby się tymi bazami tworzonymi przez użytkowników strony?

1
Harord napisał(a):

Ja mam już całe logowanie i rejestracje z pomocą spring security zrobione na bazie mysql. Czyli teoretycznie mógłbym to już zostawić tylko powinienem teraz dołączyć drugą baze z tego Postgresa, która zajmowałaby się tymi bazami tworzonymi przez użytkowników strony?

Tak właśnie.

Nie wiem jaka jest skala Twojego przedsięwzięcia i ile max. można praktycznie utworzyć schematów bazodanowych wewnątrz postgresa (czytałem, że można tworzyć tysiące). Zawsze jest opcja dostawienia kolejnego postgresa i część użytkowników będzie miała 'przestrzeń roboczą' na postgres#1, a część na postgres#2. W bazie aplikacji trzymałbyś tylko informację, na którym postgresie należy wykonać SQLa i miałbyś np. osobne pule połączeń bazodanowych per instancja postgresa.

Pewnie można kombinować zupełnie inne rozwiązanie i np. uruchamiać proces bazy H2 per sesja użytkownika i "przestrzeń roboczą" robić w oparciu o H2, a nie o postgresa.
Różne możliwości, różne problemy poboczne do rozważenia i rozwiązania :-)

edycja:
Można jeszcze inaczej, ale to już będzie jazda na grubszą skalę. Robisz sobie aplikację, które będzie orkiestratorem do jakiegoś providera cloudowego i po prostu robisz provisioning instancji bazodanowej i kroisz ziomka na grubą kasę za korzystanie z takiej instancji. Jest to rozwinięcie pomysłu nr#2 z osobnym procesem per sesja ;-)

0

Dzięki wielkie jeszcze raz!
Miłego wieczoru życzę ^^

1 użytkowników online, w tym zalogowanych: 0, gości: 1