Terminou de ler?
Eu espero.
Pronto mesmo? Adiante então!
Para os trabalhos de hoje vamos precisar do seguinte material:
- Todo o material já utilizado na parte 1
- Hibernate 4.2
- H2 Database
Vamos aos downloads:
E agora o Banco de dados:
Prefira o .zip
Resolvidos os downloads, vamos Trabalhar!
No episódio de hoje vamos fazer uma... Agenda! Rá!
Vamos começar descompactando o banco H2:
Em seguida, copie-o pra dentro do projeto que fizemos no primeiro artigo:
E não esqueça, de adicioná-lo ao build path:
Feito isso, vamos configurar um DataSource, :-)
O Spring tem uma implementação pronta, precisamos apenas configurá-la. Modifique o applicationContext.xml para que ele fique assim:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <context:annotation-config/> <context:component-scan base-package="hellospring.beans"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:~/agenda" /> <property name="username" value="sa" /> <property name="password" value="sa" /> </bean> </beans>
Perfeito né não? Se nosso assunto fosse só JDBC, estaríamos resolvidos aqui. Mas prossigamos.
Uma vez criado o datasource, vamos proceder com a adição do suporte a JPA no projeto. Descompacte o hibernate que baixamos:
Dentro da pasta descompactada você encontrará uma pasta chamada lib. Dentro dela, uma pasta chamada required, e outra chamada jpa. As outras pastas não nos interessam. Copie o conteúdo destas duas pastas para o projeto:
Não esqueça de colocar as bibliotecas no buildpath.
Agora vamos criar umas entidades persistentes aqui, ;-) crie a seguinte classe:
package hellospring.entity; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Entity; @Entity public class Contato { @Id @GeneratedValue private int id; @Column private String nome; @Column private String endereco; @Column private String telefone; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEndereco() { return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } }
Representa nossa tabela de contatos, da nossa agenda. Crie agora a seguinte classe:
package hellospring.beans; import hellospring.entity.Contato; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.stereotype.Component; @Component public class Agenda { @PersistenceContext private EntityManager em; public void salvar(Contato c) { em.getTransaction().begin(); em.persist(c); em.getTransaction().commit(); } public void excluir(int id) { em.getTransaction().begin(); em.createQuery("delete from contato c where c.id = :id")// .setParameter("id", id).executeUpdate(); em.getTransaction().commit(); } public List<contato> listar() { return em.createQuery("select c from contato c", Contato.class) .getResultList(); } }
Agora devemos retornar à configuração do xml do spring; pois para termos um EntityManager, vamos precisar de um entityManagerFactory. Deixe o applicationContext.xml deste jeito:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <context:annotation-config/> <context:component-scan base-package="hellospring.beans"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:~/agenda" /> <property name="username" value="sa" /> <property name="password" value="sa" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="true"/> </bean> </property> </bean> </beans>
Agora um último detalhe: como estamos lidando com JPA, precisamos definir um persistence.xml e nele declarar as classes mapeadas. crie a pasta META-INF dentro do seu src e nela crie o perssitence.xml:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="hello-spring-pu" transaction-type="RESOURCE_LOCAL"> <class>hellospring.entity.Contato</class> </persistence-unit> </persistence>Tudo limpo até aqui? abaixo um exemplo de saída caso tudo tenha corrido bem (até o momento):
Mar 18, 2013 9:06:12 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7ea269e2: startup date [Mon Mar 18 21:06:12 GMT-03:00 2013]; root of context hierarchy Mar 18, 2013 9:06:12 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [applicationContext.xml] Mar 18, 2013 9:06:13 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName INFO: Loaded JDBC driver: org.h2.Driver Mar 18, 2013 9:06:13 PM org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean createNativeEntityManagerFactory INFO: Building JPA container EntityManagerFactory for persistence unit 'hello-spring-pu' Mar 18, 2013 9:06:13 PM org.hibernate.annotations.common.VersionINFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final} Mar 18, 2013 9:06:13 PM org.hibernate.Version logVersion INFO: HHH000412: Hibernate Core {4.2.0.Final} Mar 18, 2013 9:06:13 PM org.hibernate.cfg.Environment INFO: HHH000206: hibernate.properties not found Mar 18, 2013 9:06:13 PM org.hibernate.cfg.Environment buildBytecodeProvider INFO: HHH000021: Bytecode provider name : javassist Mar 18, 2013 9:06:13 PM org.hibernate.ejb.Ejb3Configuration configure INFO: HHH000204: Processing PersistenceUnitInfo [ name: hello-spring-pu ...] Mar 18, 2013 9:06:13 PM org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider INFO: HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider Mar 18, 2013 9:06:14 PM org.hibernate.dialect.Dialect INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect Mar 18, 2013 9:06:14 PM org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory Mar 18, 2013 9:06:14 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory INFO: HHH000397: Using ASTQueryTranslatorFactory Mar 18, 2013 9:06:15 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute INFO: HHH000228: Running hbm2ddl schema update Mar 18, 2013 9:06:15 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute INFO: HHH000102: Fetching database metadata Mar 18, 2013 9:06:15 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute INFO: HHH000396: Updating schema Mar 18, 2013 9:06:15 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute INFO: HHH000232: Schema update complete Mar 18, 2013 9:06:15 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2e4fda99: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,agenda,sampleBean1,sampleBean2,dataSource,entityManagerFactory,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy Hello!
Você pode modificar a classe Main para executar um pequeno teste:
package hellospring; import hellospring.beans.Agenda; import hellospring.beans.SampleBean2; import hellospring.entity.Contato; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext ctx = // new ClassPathXmlApplicationContext("applicationContext.xml"); Agenda agenda = ctx.getBean(Agenda.class); Contato c = new Contato(); c.setNome("Fulano"); c.setEndereco("Rua tal"); c.setTelefone("12345678"); agenda.salvar(c);System.out.println(agenda.listar());} }
Perfeito né? ocorre que se você executar esta classe, um erro curioso e não muito óbvio vai ocorrer:
INFO: HHH000232: Schema update complete
Mar 19, 2013 10:48:50 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2df0d2a3: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,agenda,sampleBean1,sampleBean2,dataSource,entityManagerFactory,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Hibernate: select contato0_.id as id1_0_, contato0_.endereco as endereco2_0_, contato0_.nome as nome3_0_, contato0_.telefone as telefone4_0_ from Contato contato0_
[]
Percebeu? No código de testes mandamos primeiro salvar, depois listar. Mas ao listar temos um resultado vazio.
Isto ocorre porque o modo transacional não está funcionando. Sim... se não comitou, não salvou, ainda estamos no mundo relacional, ;-)
Para ativar o modo relacional, primeiro modifique seu applicationContext.xml para adicionar o suporte a transações:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <context:annotation-config/> <context:component-scan base-package="hellospring.beans"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.h2.Driver" /> <property name="url" value="jdbc:h2:~/agenda" /> <property name="username" value="sa" /> <property name="password" value="sa" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <!-- <property name="generateDdl" value="true"/> --> <property name="showSql" value="true" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>
Mas isto não é tudo, um novo erro ocorrerá:
Mar 19, 2013 11:30:49 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@397dea61: startup date [Tue Mar 19 23:30:49 GMT-03:00 2013]; root of context hierarchy Mar 19, 2013 11:30:49 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [applicationContext.xml] Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [applicationContext.xml]; nested exception is java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:412) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:243) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:127) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:93) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext. (ClassPathXmlApplicationContext.java:83) at hellospring.Main.main(Main.java:13) Caused by: java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(Unknown Source) at java.security.SecureClassLoader.defineClass(Unknown Source) at java.net.URLClassLoader.defineClass(Unknown Source) at java.net.URLClassLoader.access$100(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser$AopAutoProxyConfigurer.configureAutoProxyCreator(AnnotationDrivenBeanDefinitionParser.java:126) at org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.parse(AnnotationDrivenBeanDefinitionParser.java:84) at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:73) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1438) at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1428) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:185) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:139) at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:108) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390) ... 14 more Caused by: java.lang.ClassNotFoundException: org.aopalliance.intercept.MethodInterceptor at java.net.URLClassLoader$1.run(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) ... 36 more
Se você ganhou um desses também, leia a stacktrace: ClassNotFoundException. Sim... vamos baixar outro jar, o aopalliance.jar.
Baixe-o, copie e cole no projeto, e adicione no classpath.
Você sabe como fazer... Confio em você!
Depois disso, pra fechar com chave de ouro, modifique o Agenda.java e deixe-o desse jeito:
package hellospring.beans; import hellospring.entity.Contato; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component public class Agenda { @PersistenceContext private EntityManager em; @Transactional public void salvar(Contato c) { em.persist(c); } @Transactional public void excluir(int id) { em.createQuery("delete from Contato c where c.id = :id")// .setParameter("id", id).executeUpdate(); } public Listlistar() { return em.createQuery("select c from Contato c", Contato.class) .getResultList(); } }
Ao executar novamente seu teste, seus dados estarão sendo salvos perfeitamente.
Lindo, não?
E é isso, simples assim.
Não, sério mesmo. Só isso.
Se você olhar, seu esforço foi pouco, para um grande benefício.
E isto conclui a parte 2, now with lasers, ;-)
Vejo vocês depois, até a próxima!
muito bom Leonardo!
ResponderExcluirshow de bola, parabéns.
ResponderExcluir