Reputation: 3535
My main aim is to make use of the Spring Boot application infrastructure as much as possible to read a file and persist some objects to the database, after a lot of processing. The problem is that the processing occurs, the DAO is found and an entityManager.save() is executed, but no objects are persisted (no hibernate SQL is run).
This is my loading tool application (likely to be wrong):
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.touchcorp.touchpoint"})
@Transactional
public class ContentImportingApplication {
@Autowired
private VoucherTemplateDao voucherTemplateDao;
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication(ContentImportingApplication.class);
// ... customize app settings here
ApplicationContext ctx = app.run(args);
ContentImporter importer = new ContentImporter(ctx);
importer.importContent();
System.exit(0);
}
private static class ContentImporter {
private ApplicationContext ctx = null;
public ContentImporter(ApplicationContext ctx) {
this.ctx = ctx;
}
public void importContent() throws Exception {
String otc = readFileAsString("content-to-import");
SAXBuilder sb = new SAXBuilder();
Document jDom = sb.build(new StringReader(otc));
// lots of processng code here .. this all works
// ....
Set<Long> productIds = productDocuments.keySet();
VoucherTemplateDao dao = (VoucherTemplateDao) ctx.getBean("voucherTemplateDao");
System.out.println("do we have a dao?: " + dao);
for(Long productId : productIds) {
VoucherTemplate vt = VoucherTemplate.make(productId, "RETAILER_GROUP:*|CHANNEL:*|LOCALE:de-AT|INDUSTRY:5499", VoucherTemplate.TemplateSchema.OTC);
vt.setTemplate(productDocuments.get( productId ));
// the thing that won't persist
dao.save(vt);
}
jDom.getRootElement().addContent( new Element("w") );
}
private void findAndReplaceNestedDocs(Document jDom, Element documentInOtcContent) {
// ...code omitted: this all works too...
}
public String readFileAsString(String name) {
// ...code omitted: and so does this...
}
}
}
It makes use of various Configuration classes, but PersistenceConfig is, I think the most important:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories
@PropertySource({ "classpath:persistence.properties" })
@ComponentScan(basePackages = {"com.xxxxxcorp.xxxxxpoint.model"})
public class PersistenceConfig
{
@Autowired
private Environment env;
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "com.xxxxxcorp.xxxxxpoint.model.domain" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaDialect(jpaDialect());
em.setJpaProperties(hibernateProperties());
return em;
}
@Bean
public DataSource dataSource()
{
// assumes dbcp
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory( this.entityManagerFactory().getObject() );
transactionManager.setJpaDialect(this.jpaDialect());
return transactionManager;
}
@Bean
public JpaDialect jpaDialect() {
return new HibernateJpaDialect();
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation()
{
return new PersistenceExceptionTranslationPostProcessor();
}
Properties hibernateProperties() {
return new Properties() {
{
setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
setProperty("hibernate.globally_quoted_identifiers", "true");
}
};
}
}
The associated properties file:
# jdbc
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xxxxxxpoint?autoReconnect=true
jdbc.user=root
jdbc.pass=password
# hibernate
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=verify
As I say, there are no errors when all of this runs, just no persistence either. By the way, I have blessed the pom (under properties) with the main Application class:
<start-class>com.xxxxxcorp.xxxxxpoint.Application</start-class>
so I'm sure that I'm not starting up two contexts or two apps or anything else wierd like that. Can anyone tell me where I'm going wrong?
Upvotes: 0
Views: 814
Reputation: 3535
I thought I'd post back the solution that worked. Basically the class that I said was wrong...was wrong. Thanks to David Syer for the answer.
Here's the amended ContentImportingApplication:
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.touchcorp.touchpoint"})
public class ContentImportingApplication {
@Autowired
private VoucherTemplateDao voucherTemplateDao;
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication(ContentImportingApplication.class);
ApplicationContext ctx = app.run(args);
ContentImporter importer = (ContentImporter) ctx.getBean("contentImporter");
importer.importContent(ctx);
System.exit(0);
}
@Bean
public ContentImporter contentImporter()
{
return new ContentImporter();
}
public class ContentImporter {
@Transactional
public void importContent(ApplicationContext ctx) throws Exception {
String otc = readFileAsString("content-to-import", ctx);
SAXBuilder sb = new SAXBuilder();
Document jDom = sb.build(new StringReader(otc));
// first step: associate products to otc documents
Map<Long,String> productDocumentNames = new HashMap<Long,String>();
Map<Long,String> productDocuments = new HashMap<Long,String>();
XPathExpression<Element> xpath = XPathFactory.instance().compile("//t", Filters.element());
List<Element> ts = xpath.evaluate(jDom);
for (Element t : ts) {
String documentName = t.getAttributeValue("d");
String productId = t.getChild("p").getChild("id").getValue();
// put it in, if there is a voucher for this product
if( documentName != null) {
productDocumentNames.put(Long.valueOf(productId), documentName);
} else {
System.out.println("No voucher associated with productId: " + productId);
}
}
List<Element> ds = jDom.getRootElement().getChildren("d");
for (Element d : ds) {
// for each document we find, we'll replace the document name with the content
String documentName = d.getAttributeValue("k");
Set<Long> productIds = productDocumentNames.keySet();
for(Long productId : productIds) {
if( productDocumentNames.get( productId ).equals( documentName ) ) {
findAndReplaceNestedDocs( jDom, d );
productDocuments.put( productId, new XMLOutputter( ).outputString( d.getContent() ) );
}
}
}
Set<Long> productIds = productDocuments.keySet();
for(Long productId : productIds) {
VoucherTemplate vt = VoucherTemplate.make(productId, "RETAILER_GROUP:*|CHANNEL:*|LOCALE:de-AT|INDUSTRY:5499", VoucherTemplate.TemplateSchema.OTC);
vt.setTemplate(productDocuments.get( productId ));
voucherTemplateDao.save(vt);
}
jDom.getRootElement().addContent( new Element("w") );
}
private void findAndReplaceNestedDocs(Document jDom, Element documentInOtcContent) {
// find the nested docs
List<Element> directives = documentInOtcContent.getChildren("w");
// and replace them
for(Element directive : directives) {
List<Element> docs = jDom.getRootElement().getChildren("d");
for (Element doc : docs) {
if (directive.getAttributeValue("d").equals(doc.getAttributeValue("k"))) {
List<Element> docContents = doc.getChildren();
List<Element> clones = new ArrayList<Element>();
for(Element docContent : docContents) {
Element docContentCopy = docContent.clone();
docContentCopy.detach();
clones.add(docContentCopy);
}
if (documentInOtcContent.indexOf(directive) != -1) {
documentInOtcContent.setContent(documentInOtcContent.indexOf(directive), clones);
}
}
}
break;
}
}
public String readFileAsString(String name, ApplicationContext ctx) {
String output = null;
// working code in here
return output;
}
}
}
Upvotes: 0
Reputation: 58094
There's no transaction active when your importer runs. You should make it a @Bean
and mark its importContent()
method @Transactional
. Call the method from a CommandlineRunner
maybe.
Also, if your "main aim is to make use of the Spring Boot application infrastructure as much as possible", then why don't you throw away the rest of your JPA and DataSource
configuration (it's duplicated pretty much byte for byte in Boot)?
Upvotes: 2