Search code examples
scalascala-catscats-effectdoobie

Scala Doobie not inserting values into database


My code to insert values is:

def insert(link: entity.Link) : IO[Int] = {
    logger.info("Inserting link: " + link.toString())
    sql"insert into links (title,url,publication_date,feed_id) values (${link.getTitle},${link.getUrl},${link.getPublicationDate},${link.getFeed.getId})"
        .update
        .run
        .transact(transactor)
}

The method calling it is:

    val links = for{
        feeds <- getFeeds()
        ls: List[Link] = feeds.flatMap(rssFeedReader.readFeedItems(_).asScala)
        
        ios = ls.map(
            link => 
                linkPersistence
                    .countByUrl(link.getUrl())
                    .map(x => if(x==0) linkPersistence.insert(link) else identity(x))
            
        )

        s <- ios.sequence
    }yield(s)
    val result = links.unsafeRunSync()
    logger.info("Result: " + result.toString())

Unfortunately, I cannot use For Expressions, because IO doesn't have withFilter and I am inside a Maven project (I am migrating a Spring Boot application to Typelevel stack), so I cannot use the improved For Expressions sbt plugin.

My problem is that rows are not inserted into the database. On the log there is:

reddit_bot_1  | 2021-04-23 13:06:57.805  INFO 1 --- [text-global-167] r.i.repository.LinkPersistence           : Inserting link: Link{id=0, feed=Feed{id=79, url='https://ytali.com/feed/'}, title='Se il grillo non fosse stato parlante', url='https://ytali.com/2021/04/21/se-il-grillo-non-fosse-stato-parlante/', publicationDate=Wed Apr 21 11:12:59 GMT 2021}
reddit_bot_1  | 2021-04-23 13:06:57.838  INFO 1 --- [text-global-167] r.i.repository.LinkPersistence           : Inserting link: Link{id=0, feed=Feed{id=82, url='https://cronacheletterarie.com/feed/'}, title='Scrivere come in una serie tv', url='https://www.cronacheletterarie.com/2021/04/22/scrivere-come-in-una-serie-tv-due-nuovi-thriller-italiani/', publicationDate=Thu Apr 22 06:50:30 GMT 2021}
reddit_bot_1  | 2021-04-23 13:06:57.993  INFO 1 --- [nio-8080-exec-1] reddit_bot.service.LinkUpdater           : Result: List(1, 1, 1, 1, 1, 1, 1, IO$970148716, IO$398844220, IO$2051359159, IO$1102460161, IO$1646104176, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, IO$1525243662, 1, 1, 1, 1, 1, 1, 1, 1, 1, IO$183629085, IO$2057291647, IO$1962915922, IO$1686018284, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, IO$1629671523, IO$1703036679, IO$1823891149, IO$2090799695, IO$189359223, IO$2089070694, IO$572184845, IO$638827115, IO$1723402242, IO$1063752499, IO$1262614371, IO$1882901634, IO$1592599455, IO$1561386362, IO$422495060, IO$1071010351, IO$135298961, IO$1395689700, IO$1022084533, IO$824291117, IO$977707631, IO$1835432555, IO$264428575, IO$858145293, IO$1014815708, IO$1654086426, IO$2074886515, IO$2048606753, IO$551801989, IO$2029261832, IO$1640654268, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, IO$1096825326, IO$2132380347, IO$664110340, IO$364966863, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, IO$1429149648, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, IO$1724908444, IO$1835403239, IO$2133511951, IO$1385617069, IO$876405632, IO$1773704159, IO$1078164304, IO$604307019, IO$2004298152, 1, IO$1526495986, 1, 1, 1, 1, 1, 1, 1, 1, 1)

The transactor is created like this:

def transactor() = Transactor.fromDriverManager[IO](
  "com.mysql.cj.jdbc.Driver",
  "jdbc:mysql://database:3306/redditbot",
  "redditbot",
  "redditbot"
)

UPDATE

As suggested by @luis-miguel-mejía-suárez I modified my pom.xml file like this:

         <plugin>
            <groupId>net.alchim31.maven</groupId>
            <artifactId>scala-maven-plugin</artifactId>
            <version>4.4.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>testCompile</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <scalaVersion>${scala.version}</scalaVersion>
                <sourceDir>src/main/scala</sourceDir>
                <compilerPlugins>
                    <compilerPlugin>
                        <groupId>com.olegpy</groupId>
                        <artifactId>better-monadic-for_2.13</artifactId>
                        <version>0.3.1</version>
                    </compilerPlugin>
                </compilerPlugins>
            </configuration>
        </plugin>

But the problem remains.


Solution

  • As written by @LuisMiguelMejíaSuárez

    As the error clearly says, IO does not have a withFilter method. (You can check the scaladoc here). When you put the type explicitly, you are basically filtering all elements that match such type. And, since the method does not exists, it won't compile. - And no, I do not know any workaround.

    But, I can think on at least ne reason of why it should not have it. IO is not exactly a "container" of elements, like List, since it only is a description of a computation, and if you ant to see it like a container it will only had one element, like Option. But, unlike the former, there is not concept of empty IO. Thus, filtering an IO would not make sense.

    The workaround that I have found is moving the filter inside another function :

    def insertIfNotPresent(link: Link, foundLinksCount: Int): IO[Option[Int]] = 
        foundLinksCount match{
            case 0 => linkPersistence.insert(link).map(Option(_))
            case _ => IO.pure(None)
        }
    

    The calling code becomes:

    for{
      foundLinksCount: Int <- linkPersistence.countByUrl(link.getUrl())
      n: Option[Int] <- insertIfNotPresent(link, foundLinksCount)
    }yield(n)