Implementacja bazy danych przy pomocy Spring Boot

Po co nam właściwie mongo?

  • Znacznie bardziej wydajna od relacyjnych baz danych
  • Znacznie łatwiejsza obsługa zapytań
  • Przyzwoita liczba narzędzi w porównaniu do RDBMS
  • Nie potrzebuje wydajnego serwera do funkcjonowania
  • Znaczne możliwości skalowania zasobów i dobra kooperacja wielu serwerów współpracującyh ze sobą

Wady?

  • Drastycznie zmniejszone bezpieczeństwo (transakcje mocno ograniczone)
  • Nie ma żadnego stałego schematu bazy danych
  • Łamie zasady ACID
  • Baza danych jest uzależniona od aplikacji, która z nią współpracuje
  • Bardzo łatwo zepsuć spójność bazy danych

 W naszym projekcie wykorzystaliśmy Framework Spring, który zawiera dodatkowe biblioteki do obsługi bazy danych Mongo

 Projekt bazy danych

{
    "_id": "1",
    "_class": "pl.edu.amu.wmi.wtf.fbs.be.entity.User",
    "nickname": "Cukiernik",
    "firstName": "Karol",
    "lastName": "W",
    "country": "Poland",
    "address": "ul. Franciszkańska 44 Kraków",
    "zipcode": "62-063",
    "email": "karolw@gmail.com",
    "userstatus": "ONLINE",
    "style": 2,
    "age": 13,
    "aboutMe": "sympatyk cukiernictwa",
    "registrationDate": {
        "$date": "2015-11-16T22:41:38.068Z"
    },
    "Rates": [
        {
            "_id": "1",
            "rating": "POSITIVE",
            "userratedId": {
                "$ref": "user",
                "$id": "0"
            },
            "postratedId": {
                "$ref": "Post",
                "$id": "1"
            }
        }
    ],
    "rateCounter": 0,
    "userComments": [
        {
            "$ref": "Comment",
            "$id": "1"
        }
    ]
}

Dokumenty mongoDB zapisywane są w formacie BSON i moją podobną budowę jak pliki JSON, dzięki czemu są bardzo czytelne dla człowieka. Formatu BSON został zaprojektowany, aby dostarczyć dodatkowe typy danych, zwiększyć efektywność kodowania i szybkość przeszukiwania.

Powyższy efekt uzyskaliśmy dzięki implementacji szkieletu bazy danych w Spring oraz obsłudze i wpakowaniu danych z aplikacji do bazy.

Nie każdy dokument będzie posiadać tą samą ilość pól. Jedynym obowiązkowym polem jest _id

 W Mongo nie ma jako takich zdefiniowanych relacji. Wykorzystujemy "wbudowywanie" dokumentu lub referencje dostarczane przez zewnętrzną aplikację.


Szkielet przykładowego dokumentu

@Document(collection = "User")
public class User extends ContentsRateOrganizer implements Documents{

	public enum userstatusStates {
		HIDDEN, BANNED, DELETED, ONLINE, OFFLINE;
	}

	@Id
	private String id;
	@Indexed(unique = true)
	private String nickname;
	private String firstName;
	private String lastName;
	private String country;
	private String address;
	private String zipcode;
	@Indexed(unique = true)
	private String email;
	private userstatusStates userstatus;
	private int style;
	private int age;

	private AccessData useraccountData;
	@DBRef
	private List<Post> userPosts;
	@DBRef
	private List<Comment> userComments;

	private List<Opinion> userOpinions;

	private List<Share> userShares;
	
	private List<Blog> userBlogs;


	private String aboutMe;
	@DateTimeFormat(iso = ISO.DATE_TIME)
	private Date registrationDate;
}

 W samym szkielecie decydujemy o zastosowaniu konkretnej relacji do pola w bazie danych. Mongo różni się od innych baz w postrzeganiu relacji 1:N - tutaj mamy 1:kilku (zazwyczaj wbudowanie), 1:kilkuset (zazwyczaj hybrydowa referencja), 1:kilku milionów (sama referencja).

 Jeśli nie zdefiniujemy lub wstawimy null do jakiegoś pola, to nie pojawi się ono w dokumencie w Mongo.

 Taki szkielet wypełniamy danymi i przekazujemy innym obiektom do wysłania.

Resztę niepotrzebnych danych wypełniamy wartością null. Adnotacje służą wewnetrznej implementacji zasobów w Springu.

 

Przesyłanie danych do bazy

public static MongoOperations mongoOps;
		
		static{
			mongoOps=new MongoTemplate(
							new Mongo(
							new MongoURI("mongodb://altenfrost:altenfrost@ds053794.mongolab.com:53794/testing")),"testing");
		}


	
	
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);

        try{
        	
        	
        	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new ClassPathResource("Spring-config.xml").getPath());
        	
        	UserDao userdao=context.getBean(UserDao.class);
        	BlogDao blogdao=context.getBean(BlogDao.class);
        	CommentDao commentdao=context.getBean(CommentDao.class);
        	PostDao postdao=context.getBean(PostDao.class);
        	RateDao ratedao=context.getBean(RateDao.class);
        	TagDao tagdao=context.getBean(TagDao.class);
      
        	
        	
        	Documents document=new User();
            
            User user=new User(null,0,String.valueOf(document.getNextId()),"Temp","Jan","Kowalski","Poland","ul. Grunwaldzka 44 Poznań","62-063","jankowalski@gmail.com",userstatusStates.ONLINE,1,28,null,null,null,null,null,null,null,"Przykładowy użytkownik",new Date());

            User user2=new User(null,0,String.valueOf(document.getNextId()+1l),"Cukiernik","Karol","W","Poland","ul. Franciszkańska 44 Kraków","62-063","karolw@gmail.com",userstatusStates.ONLINE,2,13,null,null,null,null,null,null,null,"sympatyk cukiernictwa",new Date());
            userdao.create(user);
            userdao.create(user2);


            document = new Tag();
            Tag tag1=new Tag(String.valueOf(document.getNextId()+1l),"kuchnia",null);
            List<Tag> taglists=new ArrayList<Tag>();
            taglists.add(tag1);
            tagdao.create(tag1);
            
            
            Rate rate=new Rate();
            document=rate;
            rate=new Rate(String.valueOf(document.getNextId()+1l),user,null,Rated.POSITIVE);
            List<Rate> ratelist=new ArrayList<>();
            ratedao.create(rate);
            
            
            
            Post post=new Post();
            document=post;
            post=new Post(ratelist,0,String.valueOf(document.getNextId()+1l),"Przykładowy wpis",new Date(),new Date(),user,null,taglists,null,"Test wpisy",null,contentstausStates.CREATED);
            postdao.create(post);
            Post post2=new Post(null,0,String.valueOf(document.getNextId()+2l),"Drugi przykładowy wpis",new Date(), new Date(),user,null,taglists,null,"Jakiś tam opis",null,contentstausStates.HIDDEN);
            postdao.create(post2);
            
            List<Post> postlist=new ArrayList<>();
            postlist.add(post);
            postlist.add(post2);
            tag1.setRelatedPosts(postlist);
            tagdao.update(tag1);
            
            user.setUserPosts(postlist);
            userdao.update(user);
            
            rate.setPostratedId(post);
            ratedao.update(rate);
            ratelist.add(rate);
            user2.setRates(ratelist);
            userdao.update(user2);
            
            Blog blog=new Blog();
            document = blog;
            blog=new Blog(String.valueOf(document.getNextId()+1l),"Blog o gotowaniu","Różne przepisy itp.","Blog kucharza","Super potrawy",new Date(),new Date(),postlist);
            blogdao.create(blog);
            
            List<Blog> bloglist=new ArrayList<Blog>();
            bloglist.add(blog);
            user.setUserBlogs(bloglist);
            userdao.update(user);
            
            Comment comment=new Comment();
            document =comment;
            comment=new Comment(null,0,String.valueOf(document.getNextId()+1l),post,new Date(),new Date(),null,"Super artykuł",null,contentstausStates.PUBLICATED);
            commentdao.create(comment);
            
            
            List<Comment> comments=new ArrayList<>();
            comments.add(comment);
            user2.setUserComments(comments);
            userdao.update(user2);
            
            

            }catch(Exception e){
        	e.printStackTrace();
        }
        

    }
  • Nawiązujemy połaczenie za pomocą zewnętrznego URI.
  • Do obsługi metod konkretnych obiektów wykorzystano komponent DAO (dostarcza jednolity interfejs do komunikacji między aplikacją a źródłem danych i nieco zwieksza bezpieczeństwo).
  • W celu ochrony spójnosci bazy danych Spring nie pozwala nam wprowadzić do bazy dokumentu, który zawieralby referencje do nieistniejacego dokumentu. Zatem na początku musimy wstawić dokument bez referencji, a później stopniowo wprowadzać update'y.
  • Mongo łamie zasadę ACID - w przypadku nagłego wyłaczenia programu (np. w jednej z środkowych linijek) nic nie monitoruje trwałosci bazy danych i nic nas nie poinformuje o niedokończonym updacie danego dokumentu! (musimy założyć perfekcyjne działanie programu)
  • Operacje przeprowadzane na Mongo są "ostateczne" - w przypadku aktualizacji jednego pola Mongo może nadpisać cały dokument. Bez zewnętrznej pomocy nie ma możliwości do archiwizacji danych.

Wnioski

  • Mongo to bardzo szybka, ale i niebezpieczna baza danych
  • Nadaje się do mniejszych projektów, nie należy jej stosować dużych aplikacjach biznesowych
  • Świetnie sprawdza się jako magazyn dużej ilości szczegółowych i niezwiązanych ze sobą danych
  • Relacje w bazie danych Mongo nie są pożądane i usuwamy je kosztem spójności i trwałości danych
  • Najlepiej, jakby tylko kilku pojedyńczych użytkowników miało jakikolwiek dostęp do bazy danych
  • Implementując rozwiazania mające na celu utrzymanie trwałosci i spójności narażamy się na wystepowanie najwiekszego problemu - duplikacji danych (np. wraz z dodaniem kolejnego użytkownika trzeba będzie aktualizować pozostałe kilka pól w innych kolekcjach)
  • Aplikacja obsługująca bazę musi być zaopatrzona w prawie wszystkie elementy umożliwiające poprawne działanie bazy m.in. walidator i autentyfikacje danych
  • Zapytania w Mongo są bardziej intuicyjne niż w SQL
Made with Slides.com