Hibernate is an ORM provider. ORM is an object-relationship mapper. Hibernate Persistent class takes the values from the Java class attributes and persists them in the database.
Problem Statement
So recently I came across an interesting issue while working on hibernate persistence. I had an object certificate which had a child collection object certificateProperties. While updating an object that refers to certificate, it keeps throwing an error as below:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.betterjavacode.model.Certificate.certificateProperties; nested exception is org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.infor.security.sts.config.model.Certificate.certificateProperties
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:765)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:734)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:518)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy213.save(Unknown Source)
It was surprising to see this error randomly popping up. I was wondering if I missed orphanRemoval tag on my object certificate for certificateProperties, but it was there. So definitely it was not the issue. Also to remember, I was using cascade = CascadeType.ALL for cascading persistence of certificate.
Solution
Now if you notice, hibernate gives enough information about the error to notice what is happening. Hibernate is complaining that it is not able to track the change for the child collection object certificateProperties while it is getting set in the parent object certificate. Hibernate requires that parent object owns a child collection object completely.
To fix this issue, the solution was simple
Old Code
public void setCertificateProperties(Set<CertificateProperty> certificateProperties)
{
this.certificateProperties = certificateProperties;
}
Solution Code
private Set<CertificateProperty> certificateProperties = new HashSet<>();
public void setCertificateProperties(Set<CertificateProperty> certificateProperties)
{
this.certificateProperties.addAll(certificateProperties);
}
Conclusion
We showed how hibernate can handle the persistence in the cascade operation. If you enjoyed this post, please subscribe to my blog here.
In this post, I will show how to use social login in a Spring Boot application. So we build an application, but we use a form-based login which is the most basic and most insecure authentication mechanism out there. How do we get over this hunch and use the latest more secure mechanism?
Social login – Tada.
Yes, with an increasing number of social networks, it has become increasingly popular and easier to build an OAuth based login mechanism using social networks. In other words, spring boot offers a solution with a social login plugin and in this post, we will show how to use social login to authenticate your users.
What will you need
IntelliJ
Java 8
Twitter/Facebook/Google/Linkedin/Github accounts
Spring Boot
Gradle
Spring Social Core
Spring offers a spring-social-core project that contains APIs to connect to user’s social accounts. Nevertheless, this library includes a connect framework that offers a solution to manage connections with social service providers. It offers support for OAuth1a and OAuth2. The simplest way to understand this library is that you create a connection factory for each social provider. A connection factory locator finds a factory to create a Sign In Provider. I will provide more details as we go along in implementing this module.
Create a Social Login Gradle Project
If you haven’t noticed from my blog posts, but I have switched from eclipse to IntelliJ for programming editor. Intellij is just smarter and easy to write code editor. So first create a Gradle project for spring boot. (Side note – if you are using IntelliJ ultimate edition, it offers a feature to create spring project.) We will be using the latest version of Spring Boot (2.0.3.RELEASE) to build this project.
I will explain each dependency added in Gradle file as we go along.
Create an entity class
We will be using a simple entity class for User with just one field name. This will look like below:
@JsonIgnoreProperties(ignoreUnknown = true)
public class User
{
public String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
Create A Social Configuration to adapt Spring Social library
Firstly, we will implement an interface SocialConfigurer that Spring social library offers. For instance, as part of this implementation, we will create connection factories for different social service providers. Also for this module, we are using InMemoryUsersConnectionRepository. You can always implement a JDBC based database user connection repository. This class will look like below:
package com.betterjavacode.reusablesociallogin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.social.UserIdSource;
import org.springframework.social.config.annotation.ConnectionFactoryConfigurer;
import org.springframework.social.config.annotation.EnableSocial;
import org.springframework.social.config.annotation.SocialConfigurer;
import org.springframework.social.config.annotation.SocialConfigurerAdapter;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.ConnectionRepository;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
import org.springframework.social.connect.mem.InMemoryUsersConnectionRepository;
import org.springframework.social.connect.support.ConnectionFactoryRegistry;
import org.springframework.social.connect.web.ProviderSignInController;
import org.springframework.social.facebook.connect.FacebookConnectionFactory;
import org.springframework.social.github.connect.GitHubConnectionFactory;
import org.springframework.social.google.connect.GoogleConnectionFactory;
import org.springframework.social.linkedin.connect.LinkedInConnectionFactory;
import org.springframework.social.security.AuthenticationNameUserIdSource;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
import org.springframework.social.twitter.connect.TwitterConnectionFactory;
import javax.inject.Inject;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:application.properties")
@EnableSocial
public class SocialConfig implements SocialConfigurer
{
@Autowired
private DataSource dataSource;
@Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment)
{
connectionFactoryConfigurer.addConnectionFactory(new TwitterConnectionFactory(environment.getProperty("spring.social.twitter.consumerKey"), environment.getProperty("spring.social.twitter.consumerSecret")));
connectionFactoryConfigurer.addConnectionFactory(new FacebookConnectionFactory(environment.getProperty("spring.social.facebook.appId"),environment.getProperty("spring.social.facebook.appSecret")));
GoogleConnectionFactory googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty("spring.social.google.appId"),environment.getProperty("spring.social.google.appSecret"));
googleConnectionFactory.setScope("profile");
connectionFactoryConfigurer.addConnectionFactory(googleConnectionFactory);
connectionFactoryConfigurer.addConnectionFactory(new GitHubConnectionFactory(environment.getProperty("spring.social.github.appId"), environment.getProperty("spring.social.github.appSecret")));
connectionFactoryConfigurer.addConnectionFactory(new LinkedInConnectionFactory(environment.getProperty("spring.social.linkedin.appId"), environment.getProperty("spring.social.linkedin.appSecret")));
}
@Override
public UserIdSource getUserIdSource()
{
return new UserIdSource() {
@Override
public String getUserId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
}
return authentication.getName();
}
};
}
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator)
{
InMemoryUsersConnectionRepository usersConnectionRepository = new InMemoryUsersConnectionRepository(
connectionFactoryLocator);
return usersConnectionRepository;
}
}
As you see in this class, I am referring to application.properties . The application.properties will look like below:
In other words, to get clientid and clientsecret , you will have to register your application with each social service provider. We will not be covering that in this post.
Create a spring web security configuration
In this class, we will extend websecurityconfigureradapter and configure HTTP security as part of spring security implementation. We also add a bean to create Sign In Providers which are part of Spring Social. In addition, we will implement this Sign In Provider to provide a facility to users to sign in with their social provider.
As you see in this class, we have a bean ProviderSignInController which will use SocialSignInAdapter.
Implement a Sign In Adapter
Above all, this is the heart of our implementation where authentication will take place and the user will be assigned a role to access the application. The user will be redirected to the application if the user successfully authenticates. This class will look like below:
As you see in getAuthentication, we pass userId and roles for token-based authentication.
If the user has not signed up with a social provider before, he will be asked to sign up and will be redirected to the application after the first time sign up.
package com.betterjavacode.reusablesociallogin.social;
import com.betterjavacode.reusablesociallogin.entity.User;
import com.betterjavacode.reusablesociallogin.util.UserHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Service;
@Service
public class SocialConnectionSignup implements ConnectionSignUp
{
@Autowired
UserHelper userHelper;
@Override
public String execute(Connection<?> connection)
{
User user = userHelper.getUser(connection);
return user.getName();
}
}
As you see in this class, we have Autowired a userHelper class, this class will have an implementation to get user details from each social provider.
Therefore, this UserHelper will look like below:
package com.betterjavacode.reusablesociallogin.util;
import com.betterjavacode.reusablesociallogin.entity.User;
import org.springframework.social.connect.Connection;
import org.springframework.social.facebook.api.Facebook;
import org.springframework.social.github.api.GitHub;
import org.springframework.social.google.api.Google;
import org.springframework.social.linkedin.api.LinkedIn;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.stereotype.Component;
@Component
public class UserHelper
{
public User getUser(Connection<?> connection)
{
User user = null;
//get the connection type
ConnectionType type = ConnectionHelper.getConnectionType(connection);
if (type.equals(ConnectionType.TWITTER)) {
user = getTwitterUser(connection);
} else if (type.equals(ConnectionType.FACEBOOK)) {
user = getFacebookUser(connection);
} else if (type.equals(ConnectionType.GOOGLE)) {
user = getGoogleUser(connection);
} else if (type.equals(ConnectionType.GITHUB)) {
user = getGithubUser(connection);
} else if (type.equals(ConnectionType.LINKEDIN)){
user = getLinkedInUser(connection);
}
return user;
}
private User getTwitterUser(Connection<?> connection)
{
User user = new User();
Twitter twitterApi = (Twitter)connection.getApi();
String name = twitterApi.userOperations().getUserProfile().getName();
user.setName(name);
return user;
}
private User getFacebookUser(Connection<?> connection)
{
User user = new User();
Facebook facebookApi = (Facebook)connection.getApi();
String [] fields = { "name" };
User userProfile = facebookApi.fetchObject("me", User.class, fields);
String name = userProfile.getName();
user.setName(name);
return user;
}
private User getGoogleUser(Connection<?> connection)
{
User user = new User();
Google googleApi = (Google) connection.getApi();
String name = googleApi.plusOperations().getGoogleProfile().getDisplayName();
user.setName(name);
return user;
}
private User getGithubUser(Connection<?> connection)
{
User user = new User();
GitHub githubApi = (GitHub) connection.getApi();
String name = githubApi.userOperations().getUserProfile().getName();
user.setName(name);
return user;
}
private User getLinkedInUser(Connection<?> connection)
{
User user = new User();
LinkedIn linkedInApi = (LinkedIn) connection.getApi();
String name = linkedInApi.profileOperations().getUserProfile().getFirstName();
user.setName(name);
return user;
}
}
Implementing a controller and views
Similarly, the last piece in this puzzle is to add a controller and corresponding views so when the user accesses the application, the user will be challenged for authentication.
However, we will add a login controller which will have three views for login, sociallogin and socialloginsuccess . This will look like below:
@Controller
public class LoginController
{
@RequestMapping(value="/login", method= RequestMethod.GET)
public String login(Model model)
{
return "login";
}
@RequestMapping(value ="/socialloginhome", method = RequestMethod.GET)
public String socialloginhome(Model model)
{
return "socialloginhome";
}
@RequestMapping(value="/socialloginsuccess", method= RequestMethod.GET)
public String socialloginsuccess(Model model)
{
return "socialloginsuccess";
}
}
Running the application
Once I build the application and run it, the flow will look like below:
You click on hereit will take you to social login screen as below:
I will choose Facebook and server-side code will redirect me to Facebook login screen. Once I enter my credentials, Facebook will redirect back me to my application as below:
Hence, we showed successful social authentication. Lastly, social login is part of any saas application you are building.
Conclusion
In conclusion, we showed how to create a social login module using the Spring boot social feature. Moreover, the code for this will be available to download here.
To use twitter data in my saas application, I was going to write my own custom Twitter client by doing a rest call. However, I found out Spring Boot offers a Twitter plugin that can be used to fetch Twitter data. Neat.
In this post, I will show some comparison of these two approaches and why one can choose over another:
Custom Twitter Client
So custom twitter client will be a standalone client which will build an HTTP entity with client secrets that are needed to authenticate with Twitter API. In this client, we will use restOperations to call API endpoint passing HTTP entity and the REST call will respond with Twitter Data Model.
There is nothing much wrong with this approach, except the fact that we will have to write an extra TwitterDataModel business object. Also, this business model should be created before we do the actual REST call.
Spring Boot Twitter Plugin
To use this plugin, first, we need to add the plugin in Gradle or maven like below:
Once we have this plugin, we can add an object of type Twitter in our code to call REST APIs.
This will look like below:
private final Twitter twitter;
public TwitterDataModel getTwitterData(long accountId)
{
String url = buildRestUrl(accountId);
ParameterizedTypeReference<HashMap<Long, TwitterDataModel>> responseType = new ParameterizedTypeReference<HashMap<Long, TwitterDataModel>>(){};
HttpEntity entity = buildHttpEntity(CLIENT_ID,CLIENT_SECRET);
Map<Long, TwitterDataModel> twitterDataModelMap = twitter.restOperations().exchange(url, HttpMethod.GET, entity, responseType).getBody();
Long keyForData = new Long(accountId);
TwitterDataModel twitterDataModel = twitterDataModelMap.get(keyForData);
return twitterDataModel;
}
public String buildRestUrl(long accountId)
{
return TWITTER_REST_ENDPOINT + accountId + TWITTER_REST_API;
}
The major advantage of this plugin is that we can get the data in Twitter Data Model that twitter offers. An then we can go on to use to handle our data.
Conclusion
In this post, I showed how we can use a Spring Boot Twitter social plugin to gather Twitter data.