@Value("#{applicationProperties['jakas.zmienna.z.pliku.properties']}")
private String zmienna;
					
				Ale adnotacje w tej prezentacji nie są Interfejsami tylko Springowymi, ale też należącymi do JPA i Hibernate.
@Service
public class CustomerServiceImpl {
	private final CustomerMapper customerMapper;
	private final CustomerDao customerDao;
	
	@Autowired
	public CustomerServiceImpl (CustomerMapper mapper, CustomerDao dao) {
		this.customerMapper = mapper;
		this.customerDao = dao;
	}
					
				W internecie poszukać dependency do POMa i podłączyć
Sciągnąć kompletny STS (Spring Tool Suite)
start.spring.io
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
public class BooksServerApplication {
	public static void main(String[] args) {
		SpringApplication.run(BooksServerApplication.class, args);
	}
}
					
				Wszystkie działają i robią to samo
Wyjątkiem jest @Repository, która dodatkowo konwertuje wyjątki dostawców bazy danych
Adnotacje pozwalają oddzielić od siebie różne warstwy aplikacji
Ułatwiają czytanie i analizowanie kodu
@RequestMapping("/services")
@RestController
public class BooksRestService {
    private final BookService bookService;
    @Autowired
    public BooksRestService(BookService bookService) {
        this.bookService = bookService;
    }
    @RequestMapping(path = "/books", method = RequestMethod.GET)
    public List<BookTo> findBooks(BookSearchCriteria bookSearchCriteria) {
        return bookService.findBooks(bookSearchCriteria);
    }
}
					
				spring-boot-starter-data-jpa
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <scope>provided</scope>
    </dependency>
                  
				spring-boot-starter-webjar wynikowy (mvn clean install) zawiera jary Tomcata Start klasy aplikacji uruchamia wbudowanego Tomcata (możliwe jest również użycie Jetty) Klasyczny serwer J2EE lub zewnętrzny Tomcat Zmiana pliku docelowego na war, niewielkie zmiany w pom.xml
A Repository represents all objects of a certain type as a conceptual set. It acts like a collection, except with more elaborate querying capability.[DDD]
REST - REpresentation State Transfer. Roy Fielding 2000.
REST to nie jest architektura, tylko pewien zespół ograniczeń, który jeśli jest zastosowany do architektury to nadaje konkretne role danym, komponentom, hyperlinkom, ...
RESTful - spełniający wszystkie punkty powyższych założeń.
@RequestMapping(path = "/cars", method = RequestMethod.GET)
public List<CarTo> findAllCars() { ... }
@RequestMapping(path = "/car", method = RequestMethod.POST)
public CarTo addCar(@RequestBody CarTo car) { ... }
@RequestMapping(path = "/car", method = RequestMethod.PUT)
public CarTo updateCar(@RequestBody CarTo car) { ... }
@RequestMapping(path = "/car/{id}", method = RequestMethod.DELETE)
public boolean deleteCar(@PathVariable("id") Long id) { ... }
					
					
@Service public class CustomerServiceImpl {
   private static final String FIND_ALL_LIBRARIES_IN_CITY_SQL =
      "SELECT l.id, l.name, l.address_id FROM Library l, Address a 
       WHERE l.address_id = a.id AND a.city = :city";
   
   @Autowired private NamedParameterJdbcOperations jdbcTemplate;
   @Autowired private LibraryRowMapper mapper;
   
   public List<LibraryTO> findAllLibrariesInCity (String cityName) {
      SqlParameterSource params = new MapSqlParameterSource("city", cityName);
      return jdbcTemplate.query(FIND_ALL_LIBRARIES_IN_CITY_SQL, params, mapper);
   }
									
				
					
@Component
public class LibraryRowMapper implements RowMapper<LibraryTO> {
   @Override
   public LibraryTO mapRow(ResultSet rs, int rowNum) throws SQLException {
      LibraryTO library = new LibraryTO();
      library.setId(rs.getLong(1));
      library.setName(rs.getString(2));
      library.setAddress(mapAddressById(rs.getLong(3)));
      return library;
   }
   
   private AddressTO mapAddressById(Long addressId) {
      if (addressId != null && Long.compare(0, addressId) != 0)
        return new AddressTO(addressId));
      return null;
   }
									
				Podstawowym zadaniem ORM jest rozwiązanie wrodzonych niezgodności pomiędzy obiektami i bazami danych
public class LibraryEntity {
	private String name;
	private String domain;
	public LibraryEntity () {
	}
	public String getName() { return name; }
	public void setName(String name) { this.name = name; }
	
	public String getDomain() { return domain; }
	public void setDomain(String domain) { this. domain = domain; }
}
					
				
@Entity
public class LibraryEntity {
	@Id
	private Long id;
	
	@Column(name = "name", length = 30, nullable = false)
	private String name;
	
	@Column(name = "domain", length = 5, nullable = true)
	private String domain;
	
	public LibraryEntity () {
	}
	
	// getters and setters
}
					
				
@Entity
@Table(name = "LIBRARY", schema = "public")
@Access(AccessType.FIELD)
public class LibraryEntity {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	@Column(name = "name", length = 30, nullable = false)
	private String name;
	
	@Lob
	@Column(nullable = false)
	private String description;
	
	private String city;
	
	@Access(AccessType.PROPERTY)
	public String getCity ()  { ... }
	
	public LibraryEntity ()  { }
	
}
					
				
@Embeddable
public class PersonalData  {
 private String firstName;
 
 private String lastName;
 
 @Column (columnDefinition=" DATE", nullable = false)
 private Date birthDate;
 public PersonalData()  {
 }
 
 // getters & setters     
 
}
					
				
@Entity
public class AuthorEntity  {
  @Embedded
  @AttributeOverrides({
       @AttributeOverride(
           name = "firstName", 
           column = @Column(
              name = "FIRST_NAME", 
              nullable = false)),
       @AttributeOverride(
           name = "lastName",
           column = @Column(
       	      name = "LAST_NAME", 
       	      nullable = false))})
  vate PersonalData personalData;
  
  ...
}
					
				@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;
@Id @SequenceGenerator(name = "bookGen", sequenceName = "BOOK_SEQ") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "bookGen") private Long id;
					
    @Id
    @TableGenerator(
         name="bookGen",
         table="ID_GEN", // opcjonalnie
         pkColumnName="GEN_KEY", // opcjonalnie
         valueColumnName="GEN_VALUE", // opcjonalnie
         pkColumnValue="BOOK_ID_GEN") // opcjonalnie
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "bookGen")
    private Long id;
					
				
@Entity
@Table(name = "CUSTOMER_CARD")
public class CustomerCardEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
	
    @Column(nullable = false)
    private String serialNumber;
    @PrePersist
    public void generateDefaultSerialNumber() {
        serialNumber = new SerialNumberGenerator().generate();
    }
}
					
				
@Entity
@Table(name = "CUSTOMER_CARD")
@EntityListeners(CustomerCardListener.class)
public class CustomerCardEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
	
    @Column(nullable = false)
    private String serialNumber;
}
					
				
public class CustomerCardListener  {
    @PrePersist
    public void generateDefaultSerialNumber
			(CustomerCardEntity customerCardEntity) {
        String serialNumber = new SerialNumberGenerator().generate();
        customerCardEntity.setSerialNumber(serialNumber);
    }
	
}
					
				String unitName = "MyPersistenceUnit"; // utwórz EntityManagerFactory emf = Persistence.createEntityManagerFactory(unitName); EntityManager em = emf.createEntityManager(); // zrób co masz do zrobienia zrobCos(em); // zamknij em.close(); emf.close();
// zapis
Product banan = new Product(1, "banan", "owoce");
em.persist(banan);
// odczyt
Product bananFromDB = em.find(Product.class, 1);
// usunięcie
Product bananFromDB = em.find(Product.class, 1);
em.remove(bananFromDB);
// zapytanie
Product product = em.createQuery(
"SELECT p FROM Product p WHERE p.category = :cat_param", Product.class)
	.setParameter("cat_param", "owoce")
	.getSingleResult();
					
				
@Entity
public class User {
	@OneToOne(
		cascade = CascadeType.ALL, // default: empty
		fetch = FetchType.LAZY, // default: EAGER
		optional = false) // default: true
	private Address address;
}
					
				
@Entity
public class Address {
}
					
				
@Entity
public class User {
	@OneToOne
	@JoinColumn(name = "ADDRESS_FK")
	private Address address;
}
					
				
@Entity
public class Address {
	@OneToOne(mappedBy = „address”)
	private User user;
}
					
					
@Entity
public class User {
	@OneToMany(
	cascade = CascadeType.ALL, // default: empty
	fetch = FetchType. EAGER) // default: LAZY
	@JoinColumn(name = "user_id")
	private Collection<Address> addresses;
}
					
				
@Entity
public class Address {
}
					
					
@Entity
public class User {
	@OneToMany(mappedBy = "user")
	private Collection<Address> addresses;
}
					
				
@Entity
public class Address {
	@ManyToOne
	@JoinColumn(name = "ADDRESS_FK", nullable = false)
	private User user;
}
					
					
@Entity
public class User {
	@ManyToMany(
		cascade = CascadeType.ALL, // default: empty
		fetch = FetchType. LAZY) // default: EAGER    
	@JoinTable(name = "USER_ADDRESS",
		joinColumns = {@JoinColumn(
			name = "USER_ID", 
			nullable = false, 
			updatable = false)},
		inverseJoinColumns = {@JoinColumn(
			name = "ADDRESS_ID", 
			nullable = false, 
			updatable = false)})
	private Collection<Address> addresses;
}
					
				
@Entity
public class Address {
    @ManyToMany(mappedBy = "user")
    private Collection<User> users;
}
					
					
@Entity
@Table(name = "BOOK")
public class BookEntity  {
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "book")
    private BookSpoilerEntity bookSpoiler;
	
}
								
				
@Entity
@Table(name = "AUTHOR")
@DiscriminatorColumn(name = "TYPE", length = 6, 
		discriminatorType = DiscriminatorType.STRING)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class AuthorEntity  {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;
	
    @Column(nullable = true, length = 30)
    protected String nickName;
}
								
				
@Entity
@DiscriminatorValue("WRITER")
public class WriterEntity extends AuthorEntity  {
    @Enumerated(EnumType.STRING)
    private LiteraryGenre literaryGenre;
	
}
						
				
@Entity
@DiscriminatorValue("PROFES")
public class ProfessorEntity extends AuthorEntity  {
    @Column(nullable = true)
    private String university;
}
						
				
@Entity
@Table(name = "BOOK_EXEMPLAR")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class BookExemplarEntity  {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;
	
    @Column(nullable = false, length = 15, unique = true)
    protected String serialNumber;
}
						
				
@Entity
@Table(name = "PAPER_BOOK")
@PrimaryKeyJoinColumn(name = "book_ex_id", referencedColumnName = "id")
public class PaperBookExemplarEntity extends BookExemplarEntity  {
    private int pagesCount;
	
    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private PaperSize paperSize;
	
    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private BookCover bookCover;
}
						
				
public interface Dao<T> {
	void create(T entity);
	T get(Serializable id);
	T load(Serializable id);
	List<T> getAll();
	void update(T entity);
	void saveOrUpdate(T entity);
	void delete(T entity);
	void delete(Serializable id);
	void deleteAll();
	long count();
	boolean exists(Serializable id);
}
					
				
@Transactional(Transactional.txType.SUPPORTS)
public abstract class AbstractDao<T> implements Dao<T> {
   
   @Autowired
   private SessionFactory sessionFactory;
   private Class<T> domainClass;
   
   protected Session getSession() { 
      return sessionFactory.getCurrentSession();
   }
   
   @Override
   public void create(T entity) {
      getSession().save(entity);
   }
   
   @Override
   @SuppressWarnings("unchecked")
   public T get(Serializable id) {
      return (T) getSession().get(getDomainClass(), id);
   }
   
   @Override
   public List<T> getAll() {
      return getSession().createQuery("from " + getDomainClassName()).list();
   }
}
					
				
@Repository
public class LibraryDaoImpl extends AbstractDao<LibraryEntity> {
   
   @SuppressWarnings("unchecked")
   public List<LibraryEntity> findByName(String name) {
      return getSession()
         .createQuery("from LibraryEntity l where l.name like :name")
         .setString("name", name + "%")
         .list();
   }
}
					
				No to jaka właściwie jest różnica między DAO a Repository?
// rozpoczyna transakcję
em.getTransaction().begin();
// wykonanie operacji
Product prodFromDb = em.find(Product.class, 1);
prodFromDb.setCategory("newCategory");
// zatwierdzenie transakcji
em.getTransaction().commit();
// ewentualne wycofanie transakcji
em.getTransaction().rollback();
					
				
@Service
@Transactional(readOnly = true)
public class LibraryServiceImpl implements LibraryService {
	@Autowired
	private LibraryRepository libraryRepository;
	
	@Autowired
	private MapperFacade mapper;
	
	@Override
	public List<LibraryTO> findAllLibraries () {
		List<LibraryEntity> libraries = libraryRepository.findAll();
		return mapper.mapAsList(libraries, LibraryTO.class);
	}
}
					
						
@EnableWebMvc
@ComponentScan("org.itsurvival.books.rest")
public static class BooksRestServiceTestConfiguration {
    @Bean
    public BookService bookService() {
        return Mockito.mock(BookService.class);
    }
}
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private BookService bookService;
@Captor
private ArgumentCaptor<BookSearchCriteria> bookSearchCriteriaCaptor;
@Captor
private ArgumentCaptor<BookTo> bookCaptor;
				
@Test
public void shouldAddNewBook() throws Exception {
    // given
    byte[] content = readFileToBytes(
		"classpath:org/itsurvival/books/rest/newBook.json");
    when(bookService.addBook(any(BookTo.class)))
		.thenAnswer(args -> args.getArguments()[0]);
    // when
    mockMvc.perform(post("/services/book")
			.content(content)
			.contentType(MediaType.APPLICATION_JSON))
            // then
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.title", is("Test title")));
    verify(bookService).addBook(bookCaptor.capture());
    BookTo book = bookCaptor.getValue();
    assertThat(book.getTitle()).isEqualTo("Test title");
    assertThat(book.getAuthor()).isEqualTo("Test author");
    assertThat(book.getYear()).isEqualTo(2008);
    assertThat(book.getGenre()).isEqualTo(Genre.IT);
    assertThat(book.getVersion()).isEqualTo(0L);
    assertThat(book.getId()).isNull();
}