You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Tony Giaccone <to...@giaccone.org> on 2019/08/13 16:28:57 UTC

Cayenne And SpringBoot

I want to look into using Cayenne with SpringBoot.  I was able to get a
basic cayenne stack up and running by implementing a ContextListener  and
on the create event starting up a Cayenne Runtime.  I was using an in
memory database and I had problems getting the ;create=true working.  My
hack was to set the strategy on the DataNode after the runtime after it
was  spun up.

Has anyone done this before?  Are there any  suggestions  on what I should
be certain to do or avoid? Should I just spin up the standard Cayenne web
filter?  Are there other choices?

Thanks for any help you can provide.


Tony

Re: Cayenne And SpringBoot

Posted by Joe Baldwin <jf...@earthlink.net>.
Andrus,

> Also just found this on GitHub [1]. Not sure how legit it is. But posting our own "canonical" SpringBoot example is probably a good idea.


On the subject of 'working examples', I could not agree more.   

Cayenne 4 is an excellent framework, and there are some excellent working examples.  However, there are also some framework-collaboration concepts that could be more easily communicated with more detailed working examples and recommended “best practices”.

I had to spend hours trying out a lot of non-working code fragments, and out-dated version examples.  The two that provided insight were Ken Anderson’s 

	"Lovely…. I guess the list doesn’t support attachments… CayenneService.java” (August 13, 2019 email), 

and  solution-1 from stackoverflow

	https://stackoverflow.com/questions/46713728/using-cayenneruntime-in-webapplication-without-web-xml/46718149#46718149

My legacy Cayenne code (based on the at-the-time - version 3 - highly recommended "BaseContext.getThreadObjectContext()”) - recently upgraded to Cayenne-4, as best as I can tell, only works strategically with the stackoverflow example (above).   I would not have been able to get an effective demo of this working without these two working examples, (i.e. just based on reference docs).

Comments:
1. These two working examples very quickly contrasted DI-Service vs ServletFilter strategies. 
2. Working examples illustrate complex collaboration scenarios much more effectively.

Succinctly:
working examples - version-aware - effective collaboration demonstrations

Hope this helps,
Joe



> On Aug 13, 2019, at 1:25 PM, Andrus Adamchik <an...@objectstyle.org> wrote:
> 
>> Cayenne is pretty generic as far as integrations go - it should work well
>> and easily with any framework. 
> 
> Yep.
> 
>>> Has anyone done this before?  Are there any  suggestions  on what I should
>>> be certain to do or avoid? Should I just spin up the standard Cayenne web
>>> filter?  Are there other choices?
> 
> 
> It's been 3 years since I tried SpringBoot, so I don't remember all the classes involved. But at the high level the approach should be the same as with Bootique:
> 
> 1. Bind ServerRuntime as an injectable *singleton* 
> 2. Figure out how to scope ObjectContexts based on the app specifics.
> 
> I'd actually avoid CayenneFilter. It is too servlet-specific and favors session scope for the context. My typical pattern for #2 is creating a simple custom service like this:
> 
> public interface ICayenneService {
>  ObjectContext sharedContext();
>  ObjectContext newContext();
> }
> 
> It provides user-friendly API around ServerRuntime, and you inject it everywhere you need a context. You'd use "sharedContext" for reads, and "newContext" for writes.
> 
> Also just found this on GitHub [1]. Not sure how legit it is. But posting our own "canonical" SpringBoot example is probably a good idea.
> 
> Andrus
> 
> [1] https://github.com/Softmotions/spring-boot-starter-cayenne
> 
>> On Aug 13, 2019, at 7:56 PM, John Huss <jo...@gmail.com> wrote:
>> 
>> Cayenne is pretty generic as far as integrations go - it should work well
>> and easily with any framework. Just create your ServerRuntime and define a
>> way to retrieve it (using ServletContext.setAttribute is typical). Then
>> you'll want to bind the runtime to each request that comes in, which is all
>> that CayenneFilter does. If CayenneFilter has worked for you, then just use
>> that. CayenneFilter is very minimal so you copy it and customize it if
>> needed.
>> 
>> On Tue, Aug 13, 2019 at 11:29 AM Tony Giaccone <to...@giaccone.org> wrote:
>> 
>>> I want to look into using Cayenne with SpringBoot.  I was able to get a
>>> basic cayenne stack up and running by implementing a ContextListener  and
>>> on the create event starting up a Cayenne Runtime.  I was using an in
>>> memory database and I had problems getting the ;create=true working.  My
>>> hack was to set the strategy on the DataNode after the runtime after it
>>> was  spun up.
>>> 
>>> Has anyone done this before?  Are there any  suggestions  on what I should
>>> be certain to do or avoid? Should I just spin up the standard Cayenne web
>>> filter?  Are there other choices?
>>> 
>>> Thanks for any help you can provide.
>>> 
>>> 
>>> Tony
>>> 
> 


Re: Cayenne And SpringBoot

Posted by Andrus Adamchik <an...@objectstyle.org>.
> Cayenne is pretty generic as far as integrations go - it should work well
> and easily with any framework. 

Yep.

>> Has anyone done this before?  Are there any  suggestions  on what I should
>> be certain to do or avoid? Should I just spin up the standard Cayenne web
>> filter?  Are there other choices?


It's been 3 years since I tried SpringBoot, so I don't remember all the classes involved. But at the high level the approach should be the same as with Bootique:

1. Bind ServerRuntime as an injectable *singleton* 
2. Figure out how to scope ObjectContexts based on the app specifics.

I'd actually avoid CayenneFilter. It is too servlet-specific and favors session scope for the context. My typical pattern for #2 is creating a simple custom service like this:

public interface ICayenneService {
  ObjectContext sharedContext();
  ObjectContext newContext();
}

It provides user-friendly API around ServerRuntime, and you inject it everywhere you need a context. You'd use "sharedContext" for reads, and "newContext" for writes.

Also just found this on GitHub [1]. Not sure how legit it is. But posting our own "canonical" SpringBoot example is probably a good idea.

Andrus

[1] https://github.com/Softmotions/spring-boot-starter-cayenne

> On Aug 13, 2019, at 7:56 PM, John Huss <jo...@gmail.com> wrote:
> 
> Cayenne is pretty generic as far as integrations go - it should work well
> and easily with any framework. Just create your ServerRuntime and define a
> way to retrieve it (using ServletContext.setAttribute is typical). Then
> you'll want to bind the runtime to each request that comes in, which is all
> that CayenneFilter does. If CayenneFilter has worked for you, then just use
> that. CayenneFilter is very minimal so you copy it and customize it if
> needed.
> 
> On Tue, Aug 13, 2019 at 11:29 AM Tony Giaccone <to...@giaccone.org> wrote:
> 
>> I want to look into using Cayenne with SpringBoot.  I was able to get a
>> basic cayenne stack up and running by implementing a ContextListener  and
>> on the create event starting up a Cayenne Runtime.  I was using an in
>> memory database and I had problems getting the ;create=true working.  My
>> hack was to set the strategy on the DataNode after the runtime after it
>> was  spun up.
>> 
>> Has anyone done this before?  Are there any  suggestions  on what I should
>> be certain to do or avoid? Should I just spin up the standard Cayenne web
>> filter?  Are there other choices?
>> 
>> Thanks for any help you can provide.
>> 
>> 
>> Tony
>> 


Re: Cayenne And SpringBoot

Posted by John Huss <jo...@gmail.com>.
Cayenne is pretty generic as far as integrations go - it should work well
and easily with any framework. Just create your ServerRuntime and define a
way to retrieve it (using ServletContext.setAttribute is typical). Then
you'll want to bind the runtime to each request that comes in, which is all
that CayenneFilter does. If CayenneFilter has worked for you, then just use
that. CayenneFilter is very minimal so you copy it and customize it if
needed.

On Tue, Aug 13, 2019 at 11:29 AM Tony Giaccone <to...@giaccone.org> wrote:

> I want to look into using Cayenne with SpringBoot.  I was able to get a
> basic cayenne stack up and running by implementing a ContextListener  and
> on the create event starting up a Cayenne Runtime.  I was using an in
> memory database and I had problems getting the ;create=true working.  My
> hack was to set the strategy on the DataNode after the runtime after it
> was  spun up.
>
> Has anyone done this before?  Are there any  suggestions  on what I should
> be certain to do or avoid? Should I just spin up the standard Cayenne web
> filter?  Are there other choices?
>
> Thanks for any help you can provide.
>
>
> Tony
>

Re: Cayenne And SpringBoot

Posted by Ken Anderson <ke...@anderhome.com>.
Lovely…. I guess the list doesn’t support attachments…

CayenneService.java:

package com.theproductlab.cayenne;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.apache.commons.logging.LogFactory;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.configuration.server.ServerRuntime;
import org.apache.commons.logging.Log;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;


@Service(value="com.theproductlab.cayenne.ICayenneService")

public class CayenneService implements ICayenneService {

	private static final Log LOGGER = LogFactory.getLog(CayenneService.class);

	private ObjectContext sharedContext;
	private ServerRuntime cayenneRuntime;

	@Value("#{systemProperties['cayenne.project.context']}")
	private String config;

	@Autowired
	private Environment environment;

	@PostConstruct
	void init() {
		
	//	String conf = config == null ? environment.getProperty("cayenne.project.context") : config ;
		
		HikariConfig confix = new HikariConfig();
		confix.setJdbcUrl(environment.getProperty("cayenne.jdbc.url"));
		confix.setUsername(environment.getProperty("cayenne.jdbc.username"));
		confix.setPassword(environment.getProperty("cayenne.jdbc.password"));
		confix.setMaximumPoolSize(Integer.valueOf( environment.getProperty("cayenne.jdbc.max_connections")));
		confix.setDriverClassName(environment.getProperty("cayenne.jdbc.driver"));
		confix.setConnectionTestQuery("SELECT 1");
		
		confix.addDataSourceProperty("socketTimeout", 1);
		confix.setMinimumIdle(0);
		confix.setIdleTimeout(1000);
		
		confix.setConnectionTimeout(20000);
		confix.setLeakDetectionThreshold(300000); //5 mins.
		HikariDataSource ds = new HikariDataSource(confix);

        cayenneRuntime = ServerRuntime.builder()
                .addConfig("cayenne-project.xml")
                .dataSource(ds)
                .build();
        
		sharedContext = cayenneRuntime.newContext();

		Runtime.getRuntime().addShutdownHook(new Thread( new Runnable() {
			
			@Override
			public void run() {
				if (cayenneRuntime != null) {
					cayenneRuntime.shutdown();
				}
			}
		}));
		
		LOGGER.info("Cayenne setup done");
		LOGGER.info(this);
	}

	@Override
	public ObjectContext sharedContext() {
		return sharedContext;
	}

	@Override
	public ObjectContext newObjectContext() {
		return cayenneRuntime.newContext();
	}
	
	@PreDestroy
	public void shutdown(){
		if (cayenneRuntime != null)
			cayenneRuntime.shutdown();
	}
}

ICayenneService.java:

package com.theproductlab.cayenne;

import org.apache.cayenne.ObjectContext;

public interface ICayenneService {

	ObjectContext sharedContext();
	ObjectContext newObjectContext();

}

My Application:

package com.theproductlab.vorjou;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.theproductlab.cayenne.CayenneService;
import com.theproductlab.cayenne.ICayenneService;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@Configuration
@SpringBootApplication
public class VorjouApplication {

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


    @Bean
    public ICayenneService cayenneService() {
    	return new CayenneService();
    }
}


> On Aug 13, 2019, at 3:08 PM, Ken Anderson <ke...@anderhome.com> wrote:
> 
> Here’s what I did - but it’s a year + old… 
> 
> 
> 
> Please note that it also integrates Hikari for connection pools.
> 
> With the above done, all you should have to do is:
> 
> 	@Autowired
> 	ICayenneService cayenneService;
> 
> Again, I probably can’t answer questions too quickly, but hopefully this helps!
> 
> Ken
> 
> 
>> On Aug 13, 2019, at 2:16 PM, John Huss <jo...@gmail.com> wrote:
>> 
>> You don't want to construct multiple ServerRuntimes, especially not one per
>> context. You just need one runtime and you can create many contexts from it.
>> 
>> On Tue, Aug 13, 2019 at 1:09 PM Emerson Castañeda <em...@gmail.com> wrote:
>> 
>>> I got this working using this approach:
>>> 
>>> *dependencies:*
>>> 
>>> //for persistency
>>> compile 'org.apache.cayenne:cayenne-server:X.Y'
>>> compile 'org.apache.cayenne:cayenne-java8:X.Y'
>>> compile 'com.oracle:ojdbc.x.y.b'
>>> 
>>> 
>>> *application.yml*
>>> 
>>> spring:
>>>   application:
>>>       name: app-api
>>>       evnpath:
>>>       cayenne_datasource:
>>>           fpa:
>>>               username: user
>>>               password: pwd
>>>               url: jdbc:oracle:thin:@server.org:1521/DEV.org
>>>               driver: oracle.jdbc.driver.OracleDriver
>>> 
>>> 
>>> *Configuration class:*
>>> 
>>> @Component
>>> public class DataProviderService {
>>> private static final Logger logger =
>>> LoggerFactory.getLogger(DataProviderService.class);
>>> List<ObjectContext> cayenneContexts = null;
>>> @Autowired
>>> private MyBeanPropertyReader myBeanPropertyReader;
>>> 
>>> private  ObjectContext cayenneContext(String user, String password,String
>>> url, String driver) {
>>> ServerRuntimeBuilder b = ServerRuntime.builder();
>>> b = b.url(url);
>>> b = b.jdbcDriver(driver);
>>> b = b.user(user);
>>> b = b.password(password);
>>> b = b.addConfigs("cayenne-ExistentFacultyPortfolio.xml");
>>> ServerRuntime cayenneRuntime = b.build();
>>> return cayenneRuntime.newContext();
>>> }
>>> 
>>> 
>>> @PostConstruct
>>> private void init(){
>>> cayenneContexts =
>>> 
>>> Arrays.asList(cayenneContext(myBeanPropertyReader.getUser(),myBeanPropertyReader.getPassword(),
>>> myBeanPropertyReader.getUrl(),myBeanPropertyReader.getDriver()));
>>> 
>>> logger.debug("DataProviderService init APPLICATION");
>>> 
>>> cayenneContexts.forEach(
>>> oc -> oc.getEntityResolver()
>>> .getDataMaps()
>>> .forEach(dm -> logger.debug("DATAMAP : "+dm.getName())));
>>> }
>>> 
>>> //any other method using the context
>>> }
>>> 
>>> *Usage:*
>>> 
>>> @Autowired
>>> private DataProviderService dataProviderService;
>>> 
>>> 
>>> *Note:*
>>> 
>>> datanode and datamap are located in the usual place: src/main/resourcesMy
>>> 
>>> On Tue, Aug 13, 2019 at 12:29 PM Tony Giaccone <to...@giaccone.org> wrote:
>>> 
>>>> I want to look into using Cayenne with SpringBoot.  I was able to get a
>>>> basic cayenne stack up and running by implementing a ContextListener  and
>>>> on the create event starting up a Cayenne Runtime.  I was using an in
>>>> memory database and I had problems getting the ;create=true working.  My
>>>> hack was to set the strategy on the DataNode after the runtime after it
>>>> was  spun up.
>>>> 
>>>> Has anyone done this before?  Are there any  suggestions  on what I
>>> should
>>>> be certain to do or avoid? Should I just spin up the standard Cayenne web
>>>> filter?  Are there other choices?
>>>> 
>>>> Thanks for any help you can provide.
>>>> 
>>>> 
>>>> Tony
>>>> 
>>> 
> 


Re: Cayenne And SpringBoot

Posted by Ken Anderson <ke...@anderhome.com>.
Here’s what I did - but it’s a year + old… 


Re: Cayenne And SpringBoot

Posted by John Huss <jo...@gmail.com>.
You don't want to construct multiple ServerRuntimes, especially not one per
context. You just need one runtime and you can create many contexts from it.

On Tue, Aug 13, 2019 at 1:09 PM Emerson Castañeda <em...@gmail.com> wrote:

> I got this working using this approach:
>
> *dependencies:*
>
> //for persistency
> compile 'org.apache.cayenne:cayenne-server:X.Y'
> compile 'org.apache.cayenne:cayenne-java8:X.Y'
> compile 'com.oracle:ojdbc.x.y.b'
>
>
> *application.yml*
>
> spring:
>     application:
>         name: app-api
>         evnpath:
>         cayenne_datasource:
>             fpa:
>                 username: user
>                 password: pwd
>                 url: jdbc:oracle:thin:@server.org:1521/DEV.org
>                 driver: oracle.jdbc.driver.OracleDriver
>
>
> *Configuration class:*
>
> @Component
> public class DataProviderService {
> private static final Logger logger =
> LoggerFactory.getLogger(DataProviderService.class);
> List<ObjectContext> cayenneContexts = null;
> @Autowired
> private MyBeanPropertyReader myBeanPropertyReader;
>
> private  ObjectContext cayenneContext(String user, String password,String
> url, String driver) {
> ServerRuntimeBuilder b = ServerRuntime.builder();
> b = b.url(url);
> b = b.jdbcDriver(driver);
> b = b.user(user);
> b = b.password(password);
> b = b.addConfigs("cayenne-ExistentFacultyPortfolio.xml");
> ServerRuntime cayenneRuntime = b.build();
> return cayenneRuntime.newContext();
> }
>
>
> @PostConstruct
> private void init(){
> cayenneContexts =
>
> Arrays.asList(cayenneContext(myBeanPropertyReader.getUser(),myBeanPropertyReader.getPassword(),
> myBeanPropertyReader.getUrl(),myBeanPropertyReader.getDriver()));
>
> logger.debug("DataProviderService init APPLICATION");
>
> cayenneContexts.forEach(
> oc -> oc.getEntityResolver()
> .getDataMaps()
> .forEach(dm -> logger.debug("DATAMAP : "+dm.getName())));
> }
>
> //any other method using the context
> }
>
> *Usage:*
>
> @Autowired
> private DataProviderService dataProviderService;
>
>
> *Note:*
>
> datanode and datamap are located in the usual place: src/main/resourcesMy
>
> On Tue, Aug 13, 2019 at 12:29 PM Tony Giaccone <to...@giaccone.org> wrote:
>
> > I want to look into using Cayenne with SpringBoot.  I was able to get a
> > basic cayenne stack up and running by implementing a ContextListener  and
> > on the create event starting up a Cayenne Runtime.  I was using an in
> > memory database and I had problems getting the ;create=true working.  My
> > hack was to set the strategy on the DataNode after the runtime after it
> > was  spun up.
> >
> > Has anyone done this before?  Are there any  suggestions  on what I
> should
> > be certain to do or avoid? Should I just spin up the standard Cayenne web
> > filter?  Are there other choices?
> >
> > Thanks for any help you can provide.
> >
> >
> > Tony
> >
>

Re: Cayenne And SpringBoot

Posted by Emerson Castañeda <em...@gmail.com>.
I got this working using this approach:

*dependencies:*

//for persistency
compile 'org.apache.cayenne:cayenne-server:X.Y'
compile 'org.apache.cayenne:cayenne-java8:X.Y'
compile 'com.oracle:ojdbc.x.y.b'


*application.yml*

spring:
    application:
        name: app-api
        evnpath:
        cayenne_datasource:
            fpa:
                username: user
                password: pwd
                url: jdbc:oracle:thin:@server.org:1521/DEV.org
                driver: oracle.jdbc.driver.OracleDriver


*Configuration class:*

@Component
public class DataProviderService {
private static final Logger logger =
LoggerFactory.getLogger(DataProviderService.class);
List<ObjectContext> cayenneContexts = null;
@Autowired
private MyBeanPropertyReader myBeanPropertyReader;

private  ObjectContext cayenneContext(String user, String password,String
url, String driver) {
ServerRuntimeBuilder b = ServerRuntime.builder();
b = b.url(url);
b = b.jdbcDriver(driver);
b = b.user(user);
b = b.password(password);
b = b.addConfigs("cayenne-ExistentFacultyPortfolio.xml");
ServerRuntime cayenneRuntime = b.build();
return cayenneRuntime.newContext();
}


@PostConstruct
private void init(){
cayenneContexts =
Arrays.asList(cayenneContext(myBeanPropertyReader.getUser(),myBeanPropertyReader.getPassword(),
myBeanPropertyReader.getUrl(),myBeanPropertyReader.getDriver()));

logger.debug("DataProviderService init APPLICATION");

cayenneContexts.forEach(
oc -> oc.getEntityResolver()
.getDataMaps()
.forEach(dm -> logger.debug("DATAMAP : "+dm.getName())));
}

//any other method using the context
}

*Usage:*

@Autowired
private DataProviderService dataProviderService;


*Note:*

datanode and datamap are located in the usual place: src/main/resourcesMy

On Tue, Aug 13, 2019 at 12:29 PM Tony Giaccone <to...@giaccone.org> wrote:

> I want to look into using Cayenne with SpringBoot.  I was able to get a
> basic cayenne stack up and running by implementing a ContextListener  and
> on the create event starting up a Cayenne Runtime.  I was using an in
> memory database and I had problems getting the ;create=true working.  My
> hack was to set the strategy on the DataNode after the runtime after it
> was  spun up.
>
> Has anyone done this before?  Are there any  suggestions  on what I should
> be certain to do or avoid? Should I just spin up the standard Cayenne web
> filter?  Are there other choices?
>
> Thanks for any help you can provide.
>
>
> Tony
>